Accessing the head from anywhere STL list - c++

In order to get the next element in the list one just needs to increase the iterator. However from any element in the list, is there a way to get directly to the head of a list? For example if iterator is pointing to the third element of a list, is there a way to get to the front of the list besides iterating backwards ?
Thanks

No, because std::list is designed to model a doubly-linked list (and is usually implemented as one), and in a doubly linked list, each element only has pointers to the previous and next element in the list.

No, and you cannot even do it just based on having one single iterator: You have no way of knowing whether the iterator is valid, and whether you are allowed to increment or decrement it!
The only way you could do it would be by comparing your iterator (presumed valid) to x.begin() and x.end(), but once you have those, you already have the iterator to the list head and the question becomes moot.
Iterators should always be thought of as coming in pairs [first, last), and containers provide such a pair with their begin()/end() member functions.

Related

Peculiar behaviour while erasing an element from from std::multimap

I was trying to remove an element from a std::multimap while I am looping over it in a thread that manipulates it . I used erase function in the following ways
When I do this
//mItr is base iterator which loops over the multimap
std::multimap<std::string, std::string>::iterator tmpItr = ++mItr;
healthyQ.erase(mItr);
mItr = tmpItr;
so that I can have validated iterator after erasing the element from multimap, the program stalls in erase() call . So I used it in the following way to get the next valid iterator :
mItr = healthyQ.erase(mItr);
It worked. It consumed a lot of time and I am still not sure where the problem can be
The second way is exactly how it is supposed to work
When you hold an iterator to a tree-based container and you erase it, it alters the pointers between the various nodes pointing to this node (and others). Even if you would know exactly what this node is (through the iterator), you are left with no indication what is the next node (and consequently, the next iterator). Because of this, the erase method first finds the next node, performs the erase, and then returns an iterator to this next node.
You can see here how removal works in a red-black tree.
You invalidated the iterator by calling erase() function. So when you capture the iterator returns from erase() and reuse later you are properly handling the iterator and avoiding iterator invalidation.

Get pointer to node in std::list or std::forward_list

I am planning to use std::list in my code, I decided not to use std::forward_list, because for deletions (I figured) the whole list will have to traversed, O(N) complexity for std::forward_list (being a single link list). However, when I looked into the documentation I noticed both the stl containers have O(N) complexity to remove an item.
http://www.cplusplus.com/reference/forward_list/forward_list/remove/
http://www.cplusplus.com/reference/list/list/remove/
After some thinking I figured out why (I think). It's because in both cases, the whole list has to be scanned to find the node first, and then delete it. Is this right?
I then looked into the "erase" and "erase_after" methods, and their complexity is "Linear in the number of elements erased (destructions).". It's because, I am passing an iterator to the node (which is kind of like a "pointer"). However, I cannot (or prefer not to) pass this iterator around in my code to access the data in the node. I am not sure if this iterator will be valid if the list is modified? Thoughts?
My question is, is there a way I can get a pointer to the node in the list. That way, I know it will be valid throughout the lifetime of my program, pass it around. And I can just look into it to get access to my data.
However, I cannot (or prefer not to) pass this iterator around in my code to access the data in the node.
Why not? Iterators are easy to use and are quite lightweight. A pointer isn't better in any way.
I am not sure if this iterator will be valid if the list is modified?
For list, any iterator will remain valid, even if the list is modified. Except, of course, if you erase the particular element that is the iterator points to. But that's kind of obvious, you can' expect to have an iterator (or pointer) to something that doesn't exist any more.
(vector is more dangerous. One small change to a vector can invalidate all its iterators.)
You can take a pointer to any individual element in the list.
list<int> iterator it = find(l.begin(), l.end(), 7); // get an iterator
int * ptr = &*it; // get a pointer to the same element.
The pointer is similar to the iterator in many respects. But the iterator is a little more powerful. An iterator can be incremented or decremented, to access neighbouring elements in the list. And an iterator can be used to delete an element from the list. A pointer cannot do either of those things.
Both the iterator and pointer remain valid as long as that particular element isn't removed.
I am not sure if this iterator will be valid if the list is modified
Yeah, in the general case, storing iterators is risky unless you keep a close eye on the operations performed on your container.
Problem is, this is just the same for a pointer. In fact, for many containers, iterators are implemented as pointers.
So either store an iterator or a pointer if you like but, either way, keep an eye on the iterator invalidation rules:
Iterator invalidation rules
For lists, an iterator is valid even if other items in the list are erased. It becomes garbage when that item the iterator references in the list is removed.
So, as long as you know the iterator you're passing around isn't being removed by some other piece of code, they're safe to hold onto. This seems fragile though.
Even if there was a construct outside of iterators to reference a node in the list, it would suffer from the same fragility.
However, you can have each node contain an std::shared_ptr to the data it stores instead of the object itself and then pass around std::weak_ptr's to those objects and check for expired before accessing those weak_ptr's.
eg
instead of
std::list<MyClass> foo;
you would have
std::list<std::shared_ptr<MyClass>> foo;
have a look here for info on weak_ptr's
is there a way I can get a pointer to the node in the list
Yes, in your particular implementation.
No, in a standard-compliant way.
If you look at the std::list documentation, there is not a single word about a node. While it is hard to imagine a different way to implement the std::list other than using a doubly linked list, there is nothing that prevents it.
You should almost never come into any contact with undocumented internals of libraries.
Adding, removing and moving the elements within the list or across several lists does not invalidate the iterators or references. An iterator is invalidated only when the corresponding element is deleted.
Source: https://en.cppreference.com/w/cpp/container/list
So a std::list<>::iterator is only invalidated when the corresponding element is deleted. So yes, as long as you make sure that the corresponding element exists (which you will anyway have to do in your scenario of storing/passing around a pointer to anything) you can save and/or pass around the iterator throughout the lifetime of your program.
Now, an iterator is nothing but a pointer in disguise. So, if you prefer to save/pass around the corresponding pointer instead of iterator, you can always first convert the iterator to the pointer as #Aaron McDaid suggested.
int * ptr = &*it; // get a pointer to the same element.

Single linked lists & time complexity

I'm trying to write my own (as close to standard as possible) single linked list implementation. However I am wondering what time complexity people expect of such a list?
Especially for inserting I am wondering how I should implement it. I've read some locations around the internet, where some say inserting is O(1) while others say O(n) - all agree that a double linked list is O(1). However I think O(1) is the case for single linked lists too?
As long as you know the preceding node you just let the preceding node point to the new new, and the new node will point towards where the preceding node did originally point to.
That said it makes me wonder how people expect insert to behave? Normally it inserts elements BEFORE the given iterator. However with a single-linked-list it is hard to do so (one would have to go through O(n) time to get the preceding element & then use above method). Is it common in such lists to make insert place items behind the current iterator? Or -probably better- is there another common function for this?
The complexity of the insertion depends on what you need to do. If you know preceding node (actually, a suitable handle to change the preceding node's "next pointer" is all you need), the complexity is O(1). If you need to find the location where to insert, the complexity is O(n).
With respect to the expectation, I would expect the insert() to behave the same as for doubly linked lists but I also realize that you can't achieve this: You either need to have a different time complexity (to find the predecessor node) or different iterator invalidation semantics (i.e., iterators to other nodes get invalidated). I think the C++ 2011 std::forward_list class template went for a different interface but retaining the guarantees on iterator validity.
To briefly explain why the iterator validity can be effected: An iterator doesn't have to only know about the current node. Instead, it could, for example, point to the predecessor's next pointer. When dereferencing the iterator, it would dereference first its pointer to the next pointer and then this pointer to get hold of the actual node. In return, it is possible to insert in front of the iterator because the iterator knows which next pointer to update. Unfortunately, this means that iterators may get invalidated because the pointer they point to may have changed and they would reference a different node (when erasing nodes, the iterator may have been moved to be entirely invalid although the node referenced is still there).

accessing std::list in the middle

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

Safe to store list::iterator for later use?

Suppose I have a list, in which no new nodes are added or deleted. However, the nodes may be shuffled around.
Is it safe to save an iterator, pointing to a node in the list, and access it at some arbitrarily later time?
Edit (followup question):
The documentation for list::splice() says that it removes elements from the argument list. Does this mean if I call splice, using the same list as arguments to the function, that existing iterators will be invalidated?
Yes.
The standard grantees that iterators into a list will not be invalidated unless the item, they point at (metaphorically speaking) is removed from the list.
From this page: http://www.sgi.com/tech/stl/List.html
Lists have the important property that insertion and splicing do not
invalidate iterators to list elements, and that even removal invalidates
only the iterators that point to the elements that are removed.
Yes, std::list iterators are just pointers to a node. You can insert, delete (other nodes), and rearrange nodes in the list and the iterator is not invalidated.