I want to list the output of my set in alphabetical order. Below is an attempt at getting to this, but it seems slow / inefficient and I haven't even finished it yet.
void ordered(ostream &os) {
bool inserted = false;
for (objects::iterator i = begin(); i != end(); ) {
for (objects::iterator x = begin(); x != end(); ++x) {
if((**i) < (**x)) { //overloaded and works
os << **i << endl;
inserted = true;
break;
}
}
if(inserted) {
++i;
}
}
}
Clearly this will only output objects that come after the first object alphabetically.
I also considered moving the objects from a set into another container but it still seems inefficient.
The std::set is an ordered container, see reference:
http://en.cppreference.com/w/cpp/container/set
std::set is an associative container that contains a sorted set of
unique objects of type Key. Sorting is done using the key comparison
function Compare. Search, removal, and insertion operations have
logarithmic complexity. Sets are usually implemented as red-black
trees.
std::set is already ordered. It looks like you merely need to use a custom comparer that compares the pointed-to values instead of the pointers themselves (which is the default):
template<typename T> struct pless {
inline bool operator()(const T* a, const T* b) const { return *a < *b; }
};
std::set<Foo*, pless<Foo> > objects;
Related
Suppose I have a struct SignalError which has an element "errName" and many other elements:
typedef struct SignalError
{
QString errName;
.....
};
I create QList of this struct:
QList<SignalError> signalErrList;
I will append the struct element to the QList using append call.
SignalError sgErr1 = {"Error_1"};
signalerrList.append(sgErr1);
Before appending the element to the list I want to check if there is any element with the same name "errName" already existing in the QList or not. If it is then I will not add the element to the list.
How do I do this?
Also, should I create a list of objects like:
QList<SignalError> signalErrList;
or create list of pointer to the object:
QList<SignalError*> signalErrList;
You should use QList<SignalError> signalErrList; not a QList of pointers.
If you don’t care about order, you should use a std::set<SignalError> which will give you deduplication automatically.
If you care about order and the O(N) search isn’t a problem, then use QList or std::vector and do
if (auto it = std::find(signalErrList.begin(), signalErrList.end(), sgErr1;
it == signalErrList.end()) {
signalErrList.push_back(std::move(sgErr1));
}
which you could (and should) name as a function:
//! A linear-time (O(n)) search followed by push-back if not found
//! API modeled on https://en.cppreference.com/w/cpp/container/set/emplace
template <typename Container, typename Value>
std::pair<typename Container::iterator, bool>
emplaceBackLinearUnique(Container& c, Value&& v) {
if (auto it = std::find(c.begin(), c.end(), v); it != c.end()) {
// Return iterator to the found one and say that it wasn't emplaced.
return { it, false };
}
// Not found:
c.emplace_back(std::forward<Value>(v));
// Return iterator to the new last element and report that it was emplaced:
return { c.begin() + c.size() - 1, true };
}
which lets you replace. your use case with this.:
emplaceBackLinearUnique(signalErrList, sgErr1);
given two vectors
std::vector<SomeStruct> items; //1'000'000 items
std::vector<int> selection; //900'000 unique indices in ascending order
where selection contains valid indices into items, how can I shrink items efficiently to only contain the elements that are initially indexed by selection?
I am going to write this answer in reverse. Bear with me, I hope you will understand.
Lets first write a wrapper that lets us iterate only selected items:
#include <iostream>
#include <vector>
struct SomeStruct {};
struct selected_item {
std::vector<SomeStruct>& items;
std::vector<size_t>& selection;
struct iterator {
std::vector<SomeStruct>& items;
std::vector<size_t>::iterator selection_iterator;
SomeStruct& operator *(){
return items[*selection_iterator];
}
iterator& operator++(){
++selection_iterator;
return *this;
}
bool operator!=(const iterator& other){
return selection_iterator != other.selection_iterator;
}
};
iterator begin() { return {items,selection.begin()}; }
iterator end() { return {items,selection.end()};}
};
int main() {
std::vector<SomeStruct> items{{},{},{},{}};
std::vector<size_t> selection{1,3};
for (auto& i : selected_item{items,selection}){
std::cout << "item selected\n";
}
}
Using that you can now write a loop that moves selected items from items into a new vector, then move that new vector into items:
int main() {
std::vector<SomeStruct> items{{},{},{},{}};
std::vector<size_t> selection{1,3};
std::vector<SomeStruct> temp_items;
temp_items.reserve(selection.size());
for (auto& i : selected_item{items,selection}){
temp_items.emplace_back(std::move(i));
}
items = std::move(temp_items);
}
Supposed SomeStruct can be moved, this will not copy any SomeStruct. However, also moving is not for free. Depending on why you actually want to remove elements from items (why not populate a vector of selected items in the first place, instead of populating a vector of indices?) you can also consider to skip the moving altogether and use only the above wrapper to do whatever you want to do with the selected items. As 90% of the items are selected, it might be that the savings in memory and more efficient element access (due to a smaller vector) does not outweigh the moving, so you might as well directly do:
int main() {
std::vector<SomeStruct> items{{},{},{},{}};
std::vector<size_t> selection{1,3};
for (auto& i : selected_item{items,selection}){
do_something_with_selected_item(i);
}
}
Another option would be to actually erase elements from items. I did not consider it because I expect it to be rather costly. I might be wrong about that. As always, to know what is more efficient you need to measure.
PS: The wrapper is tested with gcc. I find it a little annoying to write custom iterators, not sure if it needs eg an operator== or a post-increment. I only implemented what was necessary to make gcc happy.
Read the following statement somewhere:
An additional hash table can be used to make deletion fast in
min-heap.
Question> How to combine priority_queue and unordered_map so that I can implement the idea above?
#include <queue>
#include <unordered_map>
#include <iostream>
#include <list>
using namespace std;
struct Age
{
Age(int age) : m_age(age) {}
int m_age;
};
// Hash function for Age
class HashAge {
public:
const size_t operator()(const Age &a) const {
return hash<int>()(a.m_age);
}
};
struct AgeGreater
{
bool operator()(const Age& lhs, const Age& rhs) const {
return lhs.m_age < rhs.m_age;
}
};
int main()
{
priority_queue<Age, list<Age>, AgeGreater> min_heap; // doesn't work
//priority_queue<Age, vector<Age>, AgeGreater> min_heap;
// Is this the right way to do it?
unordered_map<Age, list<Age>::iterator, HashAge > hashTable;
}
Question> I am not able to make the following work:
priority_queue<Age, list<Age>, AgeGreater> min_heap; // doesn't work
I have to use list as the container b/c the iterators of list is not affected by insertion/deletion (Iterator invalidation rules)
You can't do this with the supplied priority_queue data structure:
In a priority queue you don't know where the elements are stored, so it is hard to delete them in constant time, because you can't find the elements. But, if you maintain a hash table with the location of every element in the priority queue stored in the hash table, then you can find and remove an item quickly, although I would expect log(N) time in the worst case, not constant time. (I don't recall offhand if you get amortized constant time.)
To do this you usually need to roll your own data structures, because you have to update the hash table each time an item is moved around in the priority queue.
I have some example code that does this here:
http://code.google.com/p/hog2/source/browse/trunk/algorithms/AStarOpenClosed.h
It's based on older coding styles, but it does the job.
To illustrate:
/**
* Moves a node up the heap. Returns true if the node was moved, false otherwise.
*/
template<typename state, typename CmpKey, class dataStructure>
bool AStarOpenClosed<state, CmpKey, dataStructure>::HeapifyUp(unsigned int index)
{
if (index == 0) return false;
int parent = (index-1)/2;
CmpKey compare;
if (compare(elements[theHeap[parent]], elements[theHeap[index]]))
{
// Perform normal heap operations
unsigned int tmp = theHeap[parent];
theHeap[parent] = theHeap[index];
theHeap[index] = tmp;
// Update the element location in the hash table
elements[theHeap[parent]].openLocation = parent;
elements[theHeap[index]].openLocation = index;
HeapifyUp(parent);
return true;
}
return false;
}
Inside the if statement we do the normal heapify operations on the heap and then update the location in the hash table (openLocation) to point to the current location in the priority queue.
I need to implement a queue containing unique entries(no duplicates) in C or C++. I am thinking of maintaining a reference of elements already available in queue but that seems very inefficient.
Kindly let me know your suggestions to tackle this.
How about an auxiliary data structure to track uniqueness:
std::queue<Foo> q;
std::set<std::reference_wrapper<Foo>> s;
// to add:
void add(Foo const & x)
{
if (s.find(x) == s.end())
{
q.push_back(x);
s.insert(std::ref(q.back())); // or "s.emplace(q.back());"
}
}
Or, alternatively, reverse the roles of the queue and the set:
std::set<Foo> s;
std::queue<std::reference_wrapper<Foo>> q;
void add(Foo const & x)
{
auto p = s.insert(x); // std::pair<std::set<Foo>::iterator, bool>
if (s.second)
{
q.push_back(std::ref(*s.first)); // or "q.emplace_back(*s.first);"
}
}
queuing:
use std::set to maintain your set of unique elements
add any element that you were able to add to the std::set to the std::queue
dequeueing:
remove element from std::queue and std::set
std::queue is a container adaptor and uses relatively few members of the underlying Container. You can easily implement a custom container that contains both: an unordered_map of reference_wrapper<T> and a deque<T>. It needs at least members front and push_back. Check inside that hash_map when push_back of your container is called and reject accordingly (possibly throw). To give the complete example:
#include <iostream>
#include <set>
#include <deque>
#include <queue>
#include <unordered_set>
#include <functional>
namespace std {
// partial specialization for reference_wrapper
// is this really necessary?
template<typename T>
class hash<std::reference_wrapper<T>> {
public:
std::size_t operator()(std::reference_wrapper<T> x) const
{ return std::hash<T>()(x.get()); }
};
}
template <typename T>
class my_container {
// important: this really needs to be a deque and only front
// insertion/deletion is allowed to not get dangling references
typedef std::deque<T> storage;
typedef std::reference_wrapper<const T> c_ref_w;
typedef std::reference_wrapper<T> ref_w;
public:
typedef typename storage::value_type value_type;
typedef typename storage::reference reference;
typedef typename storage::const_reference const_reference;
typedef typename storage::size_type size_type;
// no move semantics
void push_back(const T& t) {
auto it = lookup_.find(std::cref(t));
if(it != end(lookup_)) {
// is already inserted report error
return;
}
store_.push_back(t);
// this is important to not have dangling references
lookup_.insert(store_.back());
}
// trivial functions
bool empty() const { return store_.empty(); }
const T& front() const { return store_.front(); }
T& front() { return store_.front(); }
void pop_front() { lookup_.erase(store_.front()); store_.pop_front(); }
private:
// look-up mechanism
std::unordered_set<c_ref_w> lookup_;
// underlying storage
storage store_;
};
int main()
{
// reference wrapper for int ends up being silly
// but good for larger objects
std::queue<int, my_container<int>> q;
q.push(2);
q.push(3);
q.push(2);
q.push(4);
while(!q.empty()) {
std::cout << q.front() << std::endl;
q.pop();
}
return 0;
}
EDIT: You will want to make my_container a proper model of container (maybe also allocators), but this is another full question. Thanks to Christian Rau for pointing out bugs.
There is one very important point you've not mentioned in your question, and that is whether your queue of items is sorted or have some kind of ordering (called a Priority queue), or unsorted (called a plain FIFO). The solution you choose will depend only on the answer to this question.
If your queue is unsorted, then maintaining an extra data structure in addition to your queue will be more efficient. Using a second structure which is ordered in some way to maintain the contents of your queue will allow you check if an item already exists in your queue or not much quicker that scanning the queue itself. Adding to the end of an unsorted queue takes constant time and can be done very efficiently.
If your queue must be sorted, then placing the item into the queue requires you to know the item's position in the queue, which requires the queue to be scanned anyway. Once you know an item's position, you know if the item is a duplicate because if it's a duplicate then an item will already exist at that position in the queue. In this case, all work can be performed optimally on the queue itself and maintaining any secondary data structure is unnecessary.
The choice of data structures is up to you. However, for (1) the secondary data structure should not be any kind of list or array, otherwise it will be no more efficient to scan your secondary index as to scan the original queue itself.
In my program I want to use view/model pattern with view = QListView and my own model which I subclassed from QAbstractListModel. My data class looks like
class Avtomat
{
...
map<QString, State *> states;
...
};
In my model class
class AvtomatModel : public QAbstractListModel
{
...
Avtomat a;
...
};
I'm trying to overload QAbstractItemView::index function so that I'm able to provide interface for editing data map.
As index function takes int row argument I solved that problem by providing the following
State* Avtomat::pStateFromIndex(int index) const
{
map<QString, State *>::const_iterator i;
int count = 0;
for (i = states.begin(); i != states.end() && count != index; ++i)
++count;
return (*i).second;
}
so in my index function I do like this
return createIndex(row, column, a.pStateFromIndex(row));
but that seems pretty ugly because I have O(n). Can you help me to design a better way to access my map using int index?
This is a fundamental data modelling issue. What's the primary way you need to retrieve your data? By key or by index?
If you only ever access it by index (including in the model) then you're simply using an inappropriate data structure and should switch to something else like a list.
If you do need to query by key too then you have several options. There's nothing wrong with what you're doing already if efficiency isn't a huge driver, especially if the data set is small. Alternatively you could also maintain both key and index mappings to your underlying data. This is a simple and effective solution but it means you have to take the hit of managing consistency between the two and has a memory overhead which may be significant if your data set is large. Or you could use a data structure that provides access by both key and index directly. Ultimately it depends on your specific circumstances and the data domain you're working with.
There's a good summary of the Qt container classes (along with the std containers) in the documentation. The section on algorithmic complexity may be particularly interesting to you.
The other option is to use a vector to hold the data in key-value pairs. The vector can then be accessed by index or by key.
Disadvantage of this is that inserting into a vector is expensive relative to a std::map.
typedef std::pair<QString, State*> StateP;
typedef std::vector<StateP> States;
States states;
Then maintain the vector in sorted order based on a predicate that compares the first element. You can the lookup items by index in O(1) or key in O(log n).
struct StatePCompare {
bool operator()(StateP const& lhs, StateP const& rhs) const {
return (lhs.first < rhs.first);
}
};
void Avtomat::insert(QString key, State* state)
{
States::iterator i = std::lower_bound(states.begin(), states.end(), StatePCompare());
if ((i != states.end() && (i->first == key)) {
// key already exists, set the element
i->second = state;
}
else {
states.insert(i, state);
}
}
State* Avtomat::find(QString key)
{
States::iterator i = std::lower_bound(states.begin(), states.end(), StatePCompare());
if ((i != states.end() && (i->first == key)) {
return i->second;
}
return NULL;
}