Is there a way to have a tree which automatically re-sorts its elements, or when calling some method (not when inserting new element)?
I would like to have a tree, i.e. a set, of small structures like this:
struct item
{
int value;
};
The set:
set<item> items;
auto p = items.insert(...);
And I would like to have a map holding iterators to the items held in set:
map<name, items::iterator> items_map;
items_map["abc"] = p.first;
After some time I would like to do lookup on the map and get the pointer to the item held in set to increase its value.
auto iter = items_map["abc"];
iter->value++;
Now I would like the set to automatically rearrange so that it reflects the updated value, I mean I would like the set to automatically sort itself (or after calling some method).
Is there a way to do so with current containers in STL?
I would like to do lookup on the map and get the pointer to the item held in set to increase its value.
Is there a way to do so with current containers in STL?
No. Elements of a std::set may not be modified. As such, the iterators are constant, and you couldn't use them to modify the elements.
The simple way to achieve similar effect would be to remove the old element, and insert a modified copy. However, any iterator that used to point to the old element will of course be invalidated by the removal of the element and will not point to the modified one.
Is there a way to have a tree which automatically re-sorts its elements, or when calling some method (not when inserting new element)?
Sure, but there is no such structure in the standard library.
Related
I've recently read this question about node_type, and I learned that if I need to modify the key of a specific node in a map, I could extract the node, modify the key and insert the node back in the map (like in this example).
Now, let's say I need to change the key of multiple nodes of this map, and to know which nodes I need to modify, I have to iterate over the values. I think I shouldn't insert the nodes back in the map while iterating (but maybe I'm wrong, see the end of the question), so I was thinking I could iterate over the map in a first time, extract the nodes I need to modify, store those in a vector, and insert the nodes back in a second time, when I'm finished iterating the map and modifying the nodes.
I suspect I could achieve that by storing the keys in a vector, not the nodes, but since the whole point is to change the keys, I thought it could be handy to use the nodes which seem to be made for that purpose.
So, basically, here's what I have so far:
std::vector</*don't know what to put here since node_type is undefined*/> tmp;
for (auto& it : myMap) {
if(it.second->needsToBeModified()){
tmp.push_back(myMap.extract(it.first));
}
}
I can't figure out what type that tmp vector would be.
As I said before, I may be wrong about not wanting to insert back the nodes while iterating, reading this answer stating that inserting or deleting in a map while iterating doesn't invalidate iterators (apart from the deleted entry). But this feels weird, what would happen if I insert a node in the map while iterating over it, would the inserted node come up in the iterating loop? Could that lead in an infinite loop?
The vector type is simple to address, since std:map specializations expose a type alias.
std::vector<std::map<K,V>::node_type> tmp;
The more involved point is the iteration. You cannot use a simple range-based for. That is because node extraction invalidates iterators to the extracted element. And that includes the iterator used behind the scenes for iteration. Instead, a more appropriate loop would be
for (auto it = myMap.begin() ; it != myMap.end();) {
if(it->second->needsToBeModified()){
auto out = it++;
tmp.push_back(myMap.extract(out));
}
else
++it;
}
Insertion while iterating is possible (since it doesn't invalidate iterators). But if the iteration order is important (even if only for debuggability), I think an auxiliary vector like you use is appropriate.
StoryTeller's answer is correct, however it would be nice if you wouldn't have to know how to spell out the whole type. There are several ways to simplify the declaration of tmp. The first is to avoid repeating the type of myMap, by using decltype():
std::vector<decltype(myMap)::node_type> tmp;
However, this still required you to know that the type of myMap contains another type named node_type. If you didn't know that, and still wanted to declare a vector that holds the result of myMap.extract(), then you can write:
std::vector<decltype(myMap.extract(myMap.begin()->first))> tmp;
As you can see, that's unfortunately quite a bit longer to type in, but you didn't have to use any knowledge about myMap, other than how you are actually using it inside the for-loop.
I am implementing A* shortest path algorithm. The Openlist part stores all the nodes that will be visited. The list is like a priority queue that the first element has the minimum cost value and so in each iteration, we just pop up the first element and visit it. But inside the iteration, we need to loop over the neighbors of this node and check if the neighbor is in Openlist.
So that means this Openlist needs to support two types of operations:
automatic sorting
look up a node(by its ID)
The problem here is that Openlist will sort by the cost value, while look up need to be based on the ID of the neighbor node(the adjacent nodes). So I was considering using set, where the elements are the nodes. But I don't know how to look up an element by its ID in this set.
struct astar_node
{
string id;
double f; //estimated cost;
double g; //distance from source to this node;
double h; //heuristic cost from this node to target;
};
struct openlist_compare
{
bool operator()(const astar_node &node1, const astar_node &node2 ){
return node1.f < node2.f ;
}
};
std::set<astar_node, openlist_compare> Openlist;
C++ containers, like std::set, std::map, std::list, and std::vector, and others, are "single purpose", or "primitive" containers. Each one has certain, unique properties that distinguish them from other containers; otherwise there would be no reason to have all these containers in the first place, of course.
The purpose of a std::set is to look up the values in the container by value, and to store only unique values in the set, that's it.
A std::set, like other associative containers, allow you to specify an optional comparator class that specifies, in effect, what the "value" means, for each actual value in the container. And you have done that, by, in effect, defining that only the f member of your astar_node class, matters for the purposes of defining what the set contains. All other values of all other members of astar_node are ignored.
Once that's done, this is it, as far as the std::set is concerned. The set can be looked up by astar_node::f, and that's it. This is the only way to find something in the set.
As I said in the beginning, std::set, and others, are "primitive" containers, and sometimes you will need something a little bit more sophisticated, as is the case here. In this situation, you can use several containers, and combine them in the manner to achieve the desired result. There is no law against using more than one container in order to store a single set of data. There is no law that says that you must use a single container to do everything that you need to do, with a particular collection of data.
Here, as I understand it, you also need to be able to find an astar_node in the set by the id value.
Fine:
typedef std::set<astar_node, openlist_compare> openlist_t;
openlist_t Openlist;
std::map<std::string, openlist_t::iterator> OpenList_by_id;
After you insert() a new astar_node into your OpenList, insert() returns an iterator for the new element in the openlist_t. You get the iterator for the inserted astar_node. Take this iterator, and put it into OpenList_by_id, with the key being the id.
Now, when you want to find something in OpenList given an id, use the map to find the iterator, then dereference it. Problem solved.
Additionally:
remove()ing an astar_node from the std::set will also require removing its iterator from the OpenList_by_id lookup map.
Since a std::set contains unique values, you need to handle the situation where an astar_node with the same f already exists in the set, and handle this situation appropriately.
we got this map :
std::map <int, int> values;
would this function be the same as the push_back function of a Vector :
void PushBack(int value)
{
values[values.size()] = value;
}
since size returns the size of the container I thought it would be correct, according to the following scenario it is :
index 0 = 200
index 1 = 150
you want to push back 100, values.size() would return 2, right? so then, it would , just like the normal push_back go inside index 2, correct?
The whole point of maps is to lookup and store data based on a key that uniquely represents that data.
If you're doing this there's no point in using a map; you should choose another data structure that more appropriately addresses the design needs of the application.
Maps and vectors are very different.
The short version to the actual question you asked:
if all you do on your customized map is key-based lookup of already existing keys (the operator[]) and your push_back it may act like an inefficient drop-in replacement for vector where you only use the vector operator[] and push_back, yes.
The long version providing some background on why what you are doing is probably not actually what you want:
A map doesn't have an index, it has a key. A map is usually implemented as a red-black tree. Such a data structure allows for efficient lookup based on the key. You usually care about the key of a particular element, the key itself carries important information. Keys are usually not contiguous, and maps will not allocate space for keys that are not used in the map.
A vector is a contiguous block of memory. This allows for efficient indexed access. An index is not the same as a key: you generally don't care about which index a particular element gets, which index you do get depends on the order of insertion (they key is independent of the insertion order in in a map), indexes into vectors are always integer values, and you cannot have non-contiguous indexes.
If all you do in your map is your own custom push_back then to the outside it might appear to function like a vector in some contexts, and it might not in ways in other contexts (e.g. iterator invalidation).
Since you don't actually care about the key for the element that gets added in your example the choice of a map is pointless. Indexed lookup in the vector will be faster, and memory overhead will be smaller (though you could end up with memory fragmentation issues if you allocate very many objects, but that's a separate subject).
And finally, if you don't know which container class to use, vector and list are the places to start. Understand the differences between those two, and when you should use either of them, and then move on to the more advanced, specialized containers like map, set, their "multi" variants, and their "unordered" variants.
Unless you only use the map in a very special way, it won't be correct. Consider this scenario:
std::map<int, int> values;
values[1] = 42;
PushBack(7);
values now holds just one element, 7, at index 1.
The question is, of course, if you need 'push back', why use a map in the first place.
If you want push_back then consider using a std::vector. A map is an associative array, to do fast lookup by a specified type of key. They are not designed to do push_back like vector is.
Difficult to say what you want to achieve, and why you try to use map instead of vector, but better method could be:
void PushBack(int value)
{
int idx = 0;
if( values.size() ) idx = values.rbegin()->first + 1;
values[idx] = value;
}
I want to change the element in a set, so I used set<T>::iterator. However, the compiler argues "the element is const". Then I realized that set<T>::iterator is a const_iterator...
So, how I can change the element? Erase it and then insert a new one?
The elements of the set will be in sorted order. If you are allowed to modify an element, then this sorting order can not be maintained. Hence you can not modify the item. You need to erase the existing element and insert a new one.
Set elements are constant and may not be modified in place. Modification could change the ordering predicate without changing the position of the element which would violate the constraints of the data structure.
However now in the future (C++17), modifying the element without erasure is possible with the extract member function:
std::set<std::string> stringset{"a", "b", "c"};
auto node = stringset.extract(stringset.begin());
std::string& str = node.value();
str = "d";
stringset.insert(std::move(node));
There is still the cost of list operations but the object itself is not destroyed or copied.
EDIT: You cant add an element to a specific location in set. the set should be in the sorted order whatever operations you do. So you have to erase the specific element and insert the new element so that the ordering of set isnt lost.
Also read more about set!
I have a red black tree algorithm which is working fine. When a node is inserted into the tree, the insert() method returns to the caller a pointer to the node that was inserted. I store all such pointers in a STL vector.
The problem is, within the operation of the RB tree, sometimes these pointers are invalidated. For instance, there is a method that is called during a rotateleft/right that copies the values of node A into the current node and then deletes node A. Well I had a pointer to node A in that vector which is now invalid.
I thought about making a way to update the pointers in the vector as follows,
1) keep a multimap which maps node pointers to the vector indices that holds those pointers.
2) Before deleting a node, check this multimap to find all the spots in the vector that will be affected
3) iterate over the vector and change the old pointer to the new pointer
4) Update the key value in the multimap to reflect the new pointer as well.
Problem is, you can't update a key value of a map collection for obvious reasons. Also this seems like a horrible solution both for complexity and implementation reasons. Any ideas on how I can accomplish this dynamic updating of pointers?
It seems more reasonable to keep the data in some opaque data structure pointed by node, and to keep external pointers to this structures instead of nodes.
Basicly it means adding a level of indirection between the tree and actual data.
I'm not sure if this is exactly what you're trying to do, but to keep track of items added to tree/heap data structures, the following has worked for me in the past:
Store two "index" vectors in addition to the underlying tree data:
std::vector<int> item_to_tree;
std::vector<int> tree_to_item;
So, to find the index in the tree of the ith item, use item_to_tree[i]. To find the item at a particular jth tree index, use tree_to_item[j]. This is similar to storing explicit pointers, as you've done, but by making use of indices you can essentially get a bi-directional map with O(1) lookup.
Obviously, within the tree operations, you have to make sure that the mappings stay consistent. I've not thought about this for an RB tree, but definitely for other tree-like structures this just adds some O(1) complexity to each operation.
In the case of the ith item "removed" from the tree, tree_to_item no longer contains the ith item index, and I usually set item_to_tree[i] = -1, or some "empty" flag.
Hope this helps.