How can I define operator< for bidirectional iterator? ( list::iterator )
(I would like to use list and not vector.)
You can't do it directly, but you can compute std::distance(x.begin(), it1) and std::distance(x.begin(), it2) and compare those. Given that lists don't have random access, you expect to have to pay the price for such a query by having to traverse the entire list.
Edit: This will perform poorly if both iterators are near the end of the list. If you want to get more fancy, you could write some exploring algorithm that moves outwards from both iterators:
[ .... <-- it1 --> .... <-- it2 --> .... ]
You would basically keep two copies for each, fwd1/rev1 and fwd2/rev2, and you decrement the rev* iterators until you hit x.begin() and advance the fwd* iterators until you hit x.end(). If your iterator pairs are uniformly distributed, this probably has better expected runtime.
Impossible. You might need to walk until end, and for that you need to know the list of origin, which is not encoded in a list::iterator.
(You can make a function object for this purpose, though, which takes the list or origin as a constructor argument. Mind you, finding out whether one iterator is less-than another would take O(n) time.)
You can't do this because you'd have to know the start and/or end of the list in order to make such a comparison. Only random access iterators define operator<.
Supposing ++(list.end()) is not undefined behaviour and equals list.end(), there is a way. But i am not sure of this hypothesis.
If it is valid, you can define a simple algorithm to obtain the result you want.
Related
i is k in my code, just used i for the future users.
std::multiset<std::pair<Point::FT, int> > res;
res.erase(res.begin() + k, res.end());
Error:
no match for operator +
How can I achieve this functionality?
Use std::next to advance the iterator k positions
res.erase(std::next(res.begin(), k), res.end());
The reason your code fails to compile is because std::multiset iterators are BidirectionalIterators, so they only implement pre/post increment/decrement operators, so you need to walk the iterator the desired number of positions, which std::next will do for you.
The iterator operator+ requires that it operate on random access iterators, because addition for non-random-access iterators is a linear operation. The iterators used by a multiset are bidirectional, so you can't do direct addition.
#Praetorian answered the question exactly as written, but I'm going to argue that you're trying to solve the wrong problem/asking the wrong question.
If you want to use indexing in your container, you should strongly prefer vector instead (possibly sorted from time to time). Alternately maybe you know the key of the item you want to erase from to the end. Then you can use logarithmic time find to find the iterator and erase from there to the end.
If you actually need a sorted, indexable, ordered container, I would suggest boost::multi_index instead of any standard container.
I need to look up two elements in a std::map. Since my map is sparse, I might not have an entry for every key, so I use lower_bound for lookup. For sake of simplicity, let's assume that I can always find two elements like this and that they are always distinct.
The quickest solution would of course be:
auto it1 = my_map.lower_bound(k1);
auto it2 = my_map.lower_bound(k2);
However, I know that the element at index k2 is located between begin and the element at index k1. Therefore, I was thinking of using std::lower_bound for the second lookup to avoid having to search the full range again:
auto it1 = my_map.lower_bound(k1);
auto it2 = std::lower_bound(begin(my_map), it1, k2);
Any opinions on the second solution? Complexity-wise it should be better, but it's a lot less pleasant to look at than the original code and I'm wondering whether it is worth bothering at all. Also, should I expect any drawbacks due to the fact that I'm using the non-member lower_bound for the second call?
The primary drawback is that the non-member std::lower_bound has to rely on the bidirectional iterators that map provides. So while it's able to perform O(log(n)) comparisons, it still has to perform O(n) iterations.
On the other hand, the member lower_bound() is aware of the internal structure of the map, which is typically some sort of binary tree. This means that's capable of traversing in ways the standard algorithm cannot.
Why do we have 2 ways like above to search for an element in the set?
Also find algorithm can be used to find an element in a list or a vector but what would be the harm in these providing a member function as well as member functions are expected to be faster than a generic algorithm?
Why do we need remove algorithm and create all the drama about erase remove where remove will just shift the elements and then use erase to delete the actual element..Just like STL list provides a member function remove why cant the other containers just offer a remove function and be done with it?
Binary_search in STL set over set's member function find?
Why do we have 2 ways like above to search for an element in the set?
Binary search returns a bool and set::find() and iterator. In order to compare apples to apples, the algorithm to compare set::find() with is std::lower_bound() which also returns an iterator.
You can apply std::lower_bound() on an arbitrary sorted range specified by a pair of (forward / bidirectional / random access) iterators and not only on a std::set. So having std::lower_bound() is justified. As std::set happens to be a sorted range, you can call
std::lower_bound(mySet.begin(), mySet.end(), value);
but the
mySet.find(value);
call is not only more concise, it is also more efficient. If you look into the implementation of std::lower_bound() you will find something like std::advance(__middle, __half); which has different complexity depending on the iterator (whether forward / bidirectional / random access iterator). In case of std::set, the iterators are bidirectional and advancing them has linear complexity, ouch! In contrast, std::set::find() is guaranteed to perform the search in logarithmic time complexity. The underlying implementation (which is a red and black tree in case of libstdc++) makes it possible. Offering a set::find() is also justified as it is more efficient than calling std::lower_bound() on std::set.
Also find algorithm can be used to find an element in a list or a
vector but what would be the harm in these providing a member function
as well as member functions are expected to be faster than a generic
algorithm?
I don't see how you could provide a faster member function for list or vector, unless the container is sorted (or possesses some special property).
Why do we need remove algorithm and create all the drama about erase
remove where remove will just shift the elements and then use erase to
delete the actual element..Just like STL list provides a member
function remove why cant the other containers just offer a remove
function and be done with it?
I can think of two reasons.
Yes, the STL is seriously lacking many convenience functions. I often feel like I live in a begin-end hell when using algorithms on an entire container; I often proved my own wrappers that accept a container, something like:
template <typename T>
bool contains(const std::vector<T>& v, const T& elem) {
return std::find(v.begin(), v.end(), elem) != v.end();
}
so that I can write
if (contains(myVector, 42)) {
instead of
if (std::find(myVector.begin(), myVector.end(), 42) != myVector.end()) {
Unfortunately, you quite often have to roll your own or use boost. Why? Because standardization is painful and slow so the standardization committee focuses on more important things. The people on the committee often donate their free time and are not paid for their work.
Now deleting elements from a vector can be tricky: Do you care about the order of your elements? Are your elements PODs? What are your exception safety requirements?
Let's assume you don't care about the order of your elements and you want to delete the i-th element:
std::swap(myVector[i], myVector.back());
myVector.pop_back();
or even simpler:
myVector[i] = myVector.back(); // but if operator= throws during copying you might be in trouble
myVector.pop_back();
In C++11 with move semantics:
myVector[i] = std::move(myVector.back());
myVector.pop_back();
Note that these are O(1) operations instead of O(N). These are examples of the efficiency and exception safety considerations that the standard committee leaves up to you. Providing a member function and "one size fits all" is not the C++ way.
Having said all these, I repeat I wish we had more convenience functions; I understand your problem.
I'll answer part of your question. The Erase-Remove idiom is from the book “Effective STL” written by Scott Meye. As to why remove() doesn't actually delete elements from the container, there is a good answer here, I just copy part of the answer:
The key is to realize that remove() is designed to work on not just a
container but on any arbitrary forward iterator pair: that means it
can't actually delete the elements, because an arbitrary iterator pair
doesn't necessarily have the ability to delete elements.
Why STL list provides a member function remove and why can't the other containers just offer a remove function and be done with it? I think it's because the idiom is more efficient than other methods to remove specific values from the contiguous-memory containers.
I have a dummy question. I always read that C++ std::list container has constant time for inserting elements at the beginning, at the end and in the middle:
Which is the correct way to insert an element directly in the middle of a std::list?
Is maybe this one?
std::list<int> l;
l.push_back(10);
l.push_back(20);
l.push_back(30);
l.push_back(40);
l.push_back(50);
l.push_back(60);
l.insert( l.end()- l.begin() /2 ); //? is this
// inserting directly in the middle?
When we say 'inserting in the middle' do we really mean that we save linear time to go from the beginning of the list to the desired point ( traversing one by one through all linked elements in between)?
You can do the iterator maths generically like this:
std::list<int>::iterator it = l.begin();
std::advance(it, std::distance(l.begin(), l.end())/2);
l.insert(it, value);
This will work for any iterator type (except OutputIterator or InputIterator)
Of course it is way more efficient to say
std::advance(it, l.size()/2);
l.insert(it, value);
Unfortunately, l.insert(l.begin + (l.size()/2), value) won't work because list iterators aren't random access, therefore don't have operator+ defined (to prevent performance surprises!). Keep in mind that std::advance() might be a costly operation depending on iterator type (it will be slow for reverse iterators implemented on a forward-only container, e.g.).
Here, "the middle" means an arbitrary point in the list, as long as you already have an iterator referring to that point. Given that, insertion is just a matter of modifying a few pointers around the insertion point.
If you don't already have an iterator for the insertion point, and it isn't somewhere simple like the beginning or the end, then it will take linear time to walk through the list to find it.
When we say 'inserting in the middle' do we really mean that we save linear time to go from the beginning of the list to the desired point ( traversing one by one through all linked elements in between)?
Yes.
Basically, It means the list needs to just change pointers to insert a new element and not navigate the entire list or copy the contents etc.Hence the insertion is constant time, because there is no need of traversing the list or copying the containers etc.
Inserting in the "middle" of a list means inserting somewhere other than the beginning or end. But doing an insertion requires an iterator to the insertion point. Once you have such an iterator, the insertion time is constant, but getting such an iterator in the first place is a separate issue.
No, when we say "inserting in the middle" we do not mean "finding the insertion point according to whatever criteria that takes traversing of the whole or indefinite part of the list".
Is there a reason that the STL does not provide functions to return an iterator into a container via an index?
For example, let's say I wanted to insert an element into a std::list but at the nth position. It appears that I have to retrieve an iterator via something like begin() and add n to that iterator. I'm thinking it would be easier if I could just get an iterator at the nth position with something like, std::list::get_nth_iterator(n).
I suspect I have misunderstood the principles of the STL. Can anyone help explain?
Thanks
BeeBand
You can use advance() from the <iterator> header:
list<foo>::iterator iter = advance(someFooList.begin(), n);
list<foo>::iterator iter = someFooList.begin();
std::advance( iter, n);
If the iterator supports random access (like vector) it'll work quite efficiently, if it only supports increasing (or decreasing) the iterator, like list, it'll work but only as well as can be.
std::list is a linked list. So it does not support random access. To get to the nth position in the list, you have to start at the beginning and move through all the nodes until you arrive at n. This is pretty expensive (O(n)), and thus it's bad to have a method that does not suggest this expense. get_nth_iterator(n) implies getting the iterator that points to the nth node is cheap.
std::vector of course supports this directly with the [] operator, because the datastructure supports random access and so this is very inexpensive for it.
std::list isn't random-access container, so there is no reason for accessing n-th element. if you need this, consider using std::vector instead..
Generally, anything that might be costly is made a bit clumsy, so you won't do it by accident. Using your example, with a container that provides random access iterators it would be simply container.begin()+n, but for an std::list (which provides a forward iterator) you'd need to use list.begin() followed by advance().
If you want to get the Nth iterator, chances are that you just shouldn't be using std::list in the first place. Then again, if you started that sentence at "chances", it would remain nearly as true...