[] operator for std::list? - c++

I'm in a situation where I cannot use a vector because I use &element[x] then add more items so the pointer is invalidated. The problem is that std::list does not seem to overload the operator [] nor provide an at() method. Therefore the only way I see I could simulate at() is by using an iterator. Is there however a better way to do this?

You should reconsider your design probably.
Trying to emulate operator[] or at for std::list would lead to performance disaster: these operations would take O(N) and not O(1) time as std::list::iterator is Bidirectional iterator, not Random Access iterator. So if you now iterate through container and call[] or at for each element, it would lead to O(N*N) instead of O(N).
That's why these operation are not provided by std::list.

#include <iterator>
std::list<int> l;
std::list<int>::iterator it = l.begin();
std::advance(it, 37);
For non-random-access iterators (like those of list), this will take linear time, of course.

No std::list is a linked list. It is not possible to access one element directly. The only possibility it to iterate over the elements from the beginning.

Related

Container with optimized find like std::map but non associative

I have a big std::vector<int> where I have to get an iterator so that I can call other functions of it, like erase. Looping through the vector to find the element I'm searching for takes a lot of time.
std::map::find() is much faster, but I don't want to allocate memory for the second value which I'm never going to use.
Is there any single-value container with find() or anything that gives me an iterator with similar speed as std::map::find ? I couldn't find any.
You're looking for std::set or std::multiset.
You could use std::unordered_map or use the same std::vector that keeps an order of elements that you could apply standard algorithms for sorted containers as for example std::equal_range.

Delete from i-th element of std::multiset to the end

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.

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?
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.

Is there any reason that the STL does not provide functions to return an iterator via index?

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...

STL like container with O(1) performance

I couldn't find an answer but I am pretty sure I am not the first one looking for this.
Did anyone know / use / see an STL like container with bidirectional access iterator that has O(1) complexity for Insert/Erase/Lookup ?
Thank you.
There is no abstract data type with O(1) complexity for Insert, Erase AND Lookup which also provides a bi-directional access iterator.
Edit:
This is true for an arbitrarily large domain. Given a sufficiently small domain you can implement a set with O(1) complexity for Insert, Erase and Lookup and a bidirectional access iterator using an array and a doubly linked list:
std::list::iterator array[MAX_VALUE];
std::list list;
Initialise:
for (int i=0;i<MAX_VALUE;i++)
array[i] = list.end();
Insert:
if (array[value] != list.end())
array[value] = list.insert(value);
Erase:
if (array[value] != list.end()) {
array[value].erase();
array[value] = list.end();
}
Lookup:
array[value] != list.end()
tr1's unordered_set (also available in boost) is probably what you are looking for. You don't specify whether or not you want a sequence container or not, and you don't specify what you are using to give O(1) lookup (ie. vectors have O(1) lookup on index, unordered_set mentioned above has O(1) average case lookup based on the element itself).
In practice, it may be sufficient to use array (vector) and defer costs of inserts and deletes.
Delete element by marking it as deleted, insert element into bin at desired position and remember offset for larger indices.
Inserts and deletes will O(1) plus O(N) cleanup at convenient time; lookup will be O(1) average, O(number of changes since last cleanup) worst case.
Associative arrays (hashtable) have O(1) lookup complexity, while doubly linked lists have O(1) bidi iteration.
One trick I've done when messing about storage optimization is to implement a linked list with an add of O(1)[1], then have a caching operation which provides a structure with a faster O(n) lookup[2]. The actual cache takes some O(n) time to build, and I didn't focus on erase. So I 'cheated' a bit and pushed the work into another operation. But if you don't have to do a ton of adds/deletes, it's not a bad way to do it.
[1] Store end pointer and only add onto the end. No traversal required.
[2] I created a dynamic array[3] and searched against it. Since the data wasn't sorted, I couldn't binsearch against it for O(lg n) time. Although I suppose I could have sorted it.
[3]Arrays have better cache performance than lists.
Full list of all the complexity gurantees for the STL can be found here:
What are the complexity guarantees of the standard containers?
Summary:
Insert: No container gurantees O(1) for generic insert.
The only container that has a genric insert gurtantee is: the 'Associative Container'. And this is O(ln(n))
There are containers the provide limited insert gurantees
Forward sequece gurantee an insert at head of O(1)
Back sequence gurantee an insert at tail of O(1)
Erase
The Associative containers gurantee O(1) for erase (If you have an iterator).
Lookup:
If you mean element access by lookup (as no container has O(1) find capabilities).
Then Random Access container is the only container with O(1) accesses
So the answer is based on container types.
This is what the standard gurantees are defiend for how does this translate to real containers:
std::vector: Sequence, Back Sequence, Forward/Reverse/Random Container
std::deque: Sequence, Front/Back Sequence, Forward/Reverse/Random Container
std::list: Sequence, Front/Back Seuqence, Forward/Reverse Container
std::set: Sorted/Simple/Unique Associative Container, Forward Container
std::map: Sorted/Pair/Unique Associative Container, Forward Container
std::multiset: Sorted/Simple/Multiple Associative Container, Forward Container
std::multimap: Sorted/Pair/Multiple Associative Container, Forward Container
You won't be able to fit all of your requirements into one container... something's gotta give ;)
However, maybe this is interesting for you:
http://www.cplusplus.com/reference/stl/