Vector of Doubly Linked Lists of Doubly Linked List Pointers - c++

I have a vector that contains Doubly Linked Lists, (i.e std::vector< DoublyLinkedList >) and then each Doubly Linked List will contain a pointer to another Doubly Linked List in the vector.
Here is an example of what I'm talking about:
So let's say we have the following vector of Doubly Linked Lists, { {1,2,0} ,{0,2,1,5}, {2,1,0,4,5} ,{4,5,1,0}, {5,4} }.
Let's look at the first Doubly Linked List in the vector, {1,2,0}. What I want is for 1 to point to the list {1,2,0} and 2 to point to the list {2,1,0,4,5} and 0 to point to {0,2,1,5} and similarly for the other lists in the vector.
In addition to having this kind of structure I also need the pointers to point to the correct list if we permute the elements of the vector.
So, say, if in the above example I swap the first two lists in the vector, which gives:
{ {0,2,1,5},{1,2,0},{2,1,0,4,5},{4,5,1,0},{5,4} }
I would still like in the list {1,2,0} that 1 points to {1,2,0} and 2 points to {2,1,0,4,5} and 0 to {0,2,1,5}.
So I am able to implement every part up until this last part.
What I've been doing so far for this part is, before the permutation I can have all the 0's in each list point to &vector[1] and then after a permutation I would have to go through every element in each list to find 0 and point them to 0's new position, so they would point to say &vector[k].
The problem with this is I have to search each list for 0 but I do not want to do the search. So is there any way to implement this without having to do a search? (The code is in C++)

In addition to the issue you described, the other problem with storing your structures directly in a vector is that certain operations on a vector invalidate some or all existing pointers to the vector. Namely removing from, or inserting elements into the vector.
Generally, in situations like these, it's better for the vector to store pointers to objects, rather than the objects themselves. In your example, a std::vector< DoublyLinkedList *> is going to work better. Your various instances of DoubleLinkedList can store pointers to each other, directly, and moving the pointers around in the vector won't have any effect on their validity.
Of course, this solution also has some other issues to work through, such as heap management, that will have to be sorted out. But that would be a different question.

Related

Good sources to understand Containers

I'm studying containers and their different performances.
When , for a vector, I read something like
"inserting elements other than the back is slow"
but for a list:
"fast insert/delete at any point"
My questions are:
How a vector is different from a list in such a way that the two sentences above are true?
How its a vector built differently from a list?
Is because they store their elements in different memory parts?
I was searching some sources to better understand these concepts.
All examples will be linked to C++ language as I believe it is perfectly described there
Vectors
Vectors are the same as dynamic arrays with the ability to resize themselves
automatically when an element is inserted or deleted, with their
storage being handled automatically by the container. Vector elements
are placed in contiguous storage so that they can be accessed and
traversed using iterators. In vectors, data is inserted at the end.
Inserting at the end takes differential time, as sometimes there may
be a need of extending the array. Removing the last element takes only
constant time because no resizing happens. Inserting and erasing at
the beginning or in the middle is linear in time.
Vector is the dynamic array, but which can manage the memory allocated to itself. This means that you can create arrays whose length is set at runtime without using the new and delete operators (explicitly specifying the allocation and deallocation of memory).
Lists
Lists are sequence containers that allow non-contiguous memory
allocation. As compared to vector, the list has slow traversal, but once a
position has been found, insertion and deletion are quick. Normally,
when we say a List, we talk about a doubly linked list. For implementing
a singly linked list, we use a forward list.
There are 2 types of lists:
Double linked list where each element has an address of [next]
and [previous], to access first or last element you could you a specific function (e.g. in C++ front() or back() on the list - listName.front() will return you the 1st(0) element in your list), head.previous point to NULL and the tail.next point to NULL;
Single linked list where each element only has the link(knows)
about the [next] element, and the last element in this list points
to NULL.
Now let's get back to your question:
how a vector is different from a list in such a way
that the two sentences above are true? How is a vector built
differently from a list? Is it because they store their elements in
different memory parts? I was searching some sources to better
understand these concepts.
As I have mentioned there are 2 types of lists (single linked and double liked), they are good when you are going to have:
many insertion/deletion everywhere except the end of a list;
You could use vector in case you are planning to:
frequently access insert/delete elements at the end of a list;
access elements at the random position (as you could use [N] to access the element at any point, where the N is the index/position of an element.
Whereas in the List you would need to use iterators to access the element at the position/index N.
So vector is a dynamic array and they tend to perform faster when you are accessing it (since there is no additional wrapper over it, and you directly access a point in memory by the pointer).
The list is a sequence container (so this one has a wrapper over some base language functionality) that sacrifices some point in favor of additional simplicity of insertion and deletion by providing a user with some useful methods to work with its elements.
And to resolve you question, we could conclude the following:
Vector
inserting elements other than the back is slow
List
fast insert/delete at any point
This can be judged by the structure they have what they are.
Insertion is swift in List because it is a linked list and this
means what? Exactly, This means that the only change is to be taken
to achieve it is to change the pointer of the [previous ] and the
[next] item, and we are done!
Whereas in Vector it would take waaaay more time to insert an element anywhere other than at the end. This could be proven by the array concept. If you have an array of one million elements and you want to replace/delete/insert the element at the very beginning of the array it would need to change the position of each element that is coming after the altered element.
List vs Vector Image:
images were taken from this sources:
singly linked list
double linked list
double linked list 2nd image
vector
Also, try having a look here vector-vs-list-in-stl. Their comparison is well described there. + look through the-c-standard-template-library-stl, by going to the "Containers" section and checking the description and its methods.

C++ vector and list insertion

Could anybody know why inserting an element into the middle of a list is faster
than inserting an element into the middle of a vector?
I prefer to use vector but am told to use list if I can.
Anybody can explains why?
And is it always recommended to use list over vector?
If I take the question verbatim, finding the middle of an array (std::vector) is a simple operation, you divide the length by two and then round up or down to get the index. Finding the middle of a doubly linked list (std::list) requires walking through all elements. Even if you know its size, you still need to walk over half of the elements. Therefore std::vector is faster than std::list, in other words one is O(1) while the other is O(n).
Inserting at a known position requires shuffing the adjacent elements for an array and just linking in another node for a doubly linked list, as others explained here. Therefore, std::list with O(1) is faster than std::vector with O(n).
Together, to insert in the exact middle, we have O(1) + O(n) for the array and O(n) + O(1) for the doubly linked list, making inserting in the middle O(n) for both container types. All this leaves out things like CPU caches and allocator speed though, it just compares the number of "simple" operations. In summary, you need to find out how you use the container. If you really insert/delete at random positions a lot, std::list might be better. If you only do so rarely and then only read the container, a std::vector might be better. If you only have ten elements, all the O(x) is probably worthless anyway and you should go with the one you like best.
Inserting into the middle of the vector requires all the elements after the insertion point to be shuffled along to make space, potentially involving lots of copying.
The list is implemented as a linked list with each node occupying its own space in memory with references to neighboring nodes, so adding a new node just requires changing 2 references to point to the new node.
Depending on the data type you use, a vector may well perform much faster than a list. But the more complex the object is to copy, the worse a vector gets.
In simple terms, a vector is an array. So, its elements are stored in consecutive memory locations (i.e., one next to the other). The only exception is that a vector allows resizing during run-time, without causing data loss.
Now, to insert to a list, you identify the node, then create the new element (any where in memory), store the value and connect the pointers.
But in the case of the vector (array), you must physically move the elements from one cell to the other in order to create that space for a new elements. That physical movement is what causes the delay, particularly if many elements (i.e., data) needs to be moved. You are not physcially moving array elements. Rather, its their contents.
Ulrich Eckhardt's answer is pretty good. I don't have enough reputation to add a comment so I will write an answer myself. Like Ulrich said the speed of insertion in the middle for both the list and the vector is O(n) in theory. In practice, modern CPUs have a thing called "prefetcher". it's pretty good at getting contiguous data. Since the vector is contiguous in memory, moving lots of elements is pretty fast because of the prefetcher. You need to be manipulating really, really big vectors in order for them to be slower in inserting than the list. For more details check this awesome blog post:
http://gameprogrammingpatterns.com/data-locality.html

std::forward_list swap() implementation in C++11

My assumption is that in a std::list<> the swap function for the list itself is done by swapping the anchor node. The node can access the previous node and update the previous node's next pointer easily to point to the anchor of the other list; but this cannot be done in std::forward_list (well, it can be, it is just very costly).
If my assumption is correct, how is swap() implemented in std::forward_list in an efficient manner? And while we are at it, how is swap() implemented for the iterators of std::forward_list?
A std::forward_list is simply a singly-linked rather than doubly-linked list like std::list, therefore you can simply swap the head and tail pointers of the list to accomplish a swap() operation.
This is completely implementation-specific, but if the forward lists are implemented by having the class just store a pointer to the first and last linked list cells, then swap could just swap those two pointers. Since forward-linked lists don't have any back pointers, there aren't any more updates to be made and the whole operation can be done in O(1).
As for swap with iterators, this doesn't actually exchange the linked list cells between the two lists; it just has the first iterator point to the cell referenced by the second iterator and vice-versa. If you want to swap the values being pointed at, then this can be done by just modifying the objects in the linked list cells so that they change values. You don't need to rewire the lists in any way.
Hope the helps!
I think you're confused (or I am confused as to what you're asking). std::forward_list::swap(std::forward_list& other) is trivial, with each list object exchanging pointers to the head of their list (and any other member variables) - just like std::list.
The iterator object doesn't have a swap method. The contained object might, or may use the global swap method - but that operates on the objects, and doesn't mutate the list itself or its nodes.

What is the best data structure for removing an element from the middle?

The data stucture must be like a stack. Only with one difference. I want to pop from any index not only last. When I have popped element n, the elements with indexes N > n must swap to N-1. Any ideas?
P.S.
Pushing element n into the last index of the stack.
Then popping it out.
Then deleting stack[n]
is a bad idea.
I think you're looking for a linked list.
A linked list (std::list) will allow you to remove an element from the middle with O(1) complexity and automatically "pull" up the elements after it. You can use a linked list like a stack by using push_front. However you need to be aware that accessing an element in a linked list is O(n) as you would need to start at the head of the list and then walk along the links from one element to the next until you have arrived at element n (so there is no O(1) indexing)
Basically you would need to
Create an iterator
advance it to position n
Get the element from the iterator
erase the element the iterator is currently pointing to
Some example code can be found here.
You need to implement a linked list, but unlike an array, the order in a linked list is determined by a pointer in each object. So, you cannot use indices to access the elements.

keeping track of changing pointers

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.