How to create a loop using STL list in C++ - c++

This is an interview question: How to create a loop using the STL list container?
I am a newbie. I searched this question and did not find any. If this is an old question please give me the link and delete this post.
Thank you all!

How to create a loop using the STL list container?
You can't.
std::list has a beginning and an end. All access to the data structure is carefully controlled so that a standard-compliant program simply cannot produce a non-terminating list.
P.s. I'm assuming that the interviewer actually meant to say "std::list" instead of "STL list."

One possible answer is: It can happen when more than one thread is manipulating the list structure at the same time. Suppose two threads want to push_back into an already formed list. If the list already has b and a, the circular list could look like:
.--------------------------.
( )
`-> a <-> SENTINEL <-> b <-'
And one thread inserts c at the same time another inserts d. They each want to attach to the back of the SENTINEL like this:
a <-> c <-> SENTINEL
a <-> d <-> SENTINEL
However, the could end up making a loop:
.------------.
a. `-. )
`-> c <-> d <-'
<--> SENTINEL <-> b (<-> a)
The forward links are fine: b -> a -> c -> d
But the reverse links will loop: d -> c -> d ...
This can happen because the pointers of the SENTINEL is being read, dereferenced, and modified without mutual exclusion.

Related

Container that allows fast search and order at the same time

I am getting in scenarios with this problem again and again and I implement different approaches every time. Now I decided to see if the stackoverflow community could suggest something better.
Let say that I have a reconcile API, where the current set of objects in a system need to be reevaluated - and this might take some time. (Note that obtaining the list of IDs of the objects is fast, the evaluation is slow.) It is public API, reconcile could be called irresponsibly. I would like to guarantee that every object in the system is reevaluated after the last call, while at the same time I do not want to reevaluate any object more than once without need. So far so good any set, ordered or unordered will do.
This additional requirement is the key: I would like to rotate the items to prevent in case of reconcile API misuse to reevaluating the same objects that sit on the "top".
... or if I have "A B C D E F" in the system at the first call, I will schedule: "A B C D E F" for reevaluation in this order.
Lets say that A B and C was already evaluated and there are new objects G and H in the system: The new queue should look like: "D E F A B C G H" where "D E F G H A B C" will be better, but it is not critical. I do not want the queue to be "A B C D E F G H" or "D E F A B C D E F G H"
The question is what stl or boost container (or combination) to use to solve this?
IMO the best approach is, if you need anything more complicated than vector, map, unordered_map, set, then you should just default to boost::multi_index_container. Multi index container has the advantage that it is extremely powerful and flexible and can efficiently support a wide variety of lookup schemes, and it's also quite easily extensible if your needs become greater later on. I would build the entire application that way first, then if you time things and find that you need to optimize, then try to replace relevant multi index containers with optimized data structures tailored to the particular operations you need to support. This saves you an incredible amount of development time fussing over data structure decisions.
A ring would be the proper data structure for this, but I don't know of any standard implementations. You can easily simulate it using by using a std::list by maintaining iterators to iterate, insert, and detect the end though.
std::list<Item>* is = new std::list<Item>();
auto it = is->begin();
auto ir = is->end();
is->insert(ir, i);
if (++it == is->end())
it = is->begin();
This gives O(1) insert and O(1) iteration. It adds in additional branch per iteration, but that could be eliminated with a proper ring.

Why is `++` for Haskell List implemented recursively and costs O(n) time?

As I understood, a List in Haskell is a similar to a Linked-List in C language.
So for expressions below:
a = [1,2,3]
b = [4,5,6]
a ++ b
Haskell implement this in a recursive way like this:
(++) (x:xs) ys = x:xs ++ ys
The time complexity for that is O(n)..
However, I was wondering why can't I implement ++ more efficiently.
The most efficient way may be like this:
make a copy(fork) of a, let's call it a', there may be some tricks to do this in O(1) time
make the last element of a' to point to the first element of b. This can be done easily in O(1) time..
Does anyone have ideas about this? Thanks!
That's pretty much what the recursive solution does. It's the copying of a which takes O(n) (where n is the length of a. The length of b doesn't affect the complexity).
There is really no "trick" to copy a list of n elements in O(1) time.
See the copy(fork) part is the problem - the recursive solution does exactly this (and you really have to do it, because you have to adjust all the pointers for the elements in the a list.
Let's say a = [a1,a2,a3] and b is some list.
You have to make a new copy of a3 (let's call it a3') because it should now no longer point to an empty list but to the start of b.
Then you have to make a copy of the second to last element a2 too because it must point to a3' and finally - for the same reason - you have to create a new copy of a1 too (pointing to a2').
This is exactly what the recursive definition does - it's no problem with the algorithm - it's a problem with the data-structure (it's just not good with concatenation).
If you don't allow mutability and want the structure of a list you can really do nothing else.
You have this in other langs. too if they provide immutable data - for example in .net strings are immutable - so there is almost the same problem with string-concatenation as is here (if you concat lots of strings your program will perform poorly). There are workaround (StringBuilder) that will deal better with the memory footprint - but of course those are no longer immutable data-structures.
There is no way to do that concatenation in constant time, simply because the immutability of the data structure doesn't allow it.
You might think that you could do something similar to the "cons" operator (:) that adds an additional element x0 to the front of a list oldList=[x1,x2,x3] (resulting in newList=(x0:oldLIst)) without having to run through the whole list. But that's just because you don't touch the existing list oldList, but simply reference it.
x0 : ( x1 : ( x2 : ( x3 : [] ) ) )
^ ^
newList oldList
But in your case (a ++ b) we are talking about updating a reference deep within the data structure. You want to replace the [] in 1:(2:(3:[])) (the explicit form of [1,2,3]) by the new tail b. Just count the parenthesis and you'll see that we have to go deep inside to get to the []. That's always expensive because we have to duplicate the whole outer part, in order to make sure that a stays unmodified. In the resulting list, where would the old a point to in order to have the unmodified list?
1 : ( 2 : ( 3 : b ) )
^ ^
a++b b
That's impossible in the same data structure. So we need a second one:
1 : ( 2 : ( 3 : [] ) )
^
a
And that means duplicating those : nodes, which necessarily costs the mentioned linear time in the first list. The "copy(fork)" that you mentioned is therefore, differently from what you said, not in O(1).
make a copy(fork) of a, let's call it a', there may be some tricks to do this in O(1) time
When you talk about a "trick" to fork something in constant time, you probably think about not actually making a full copy, but creating a reference to the original a, with the changes stored as "annotations" (like the hint: "modification to tail: use b instead of []").
But that's what Haskell, thanks to its lazyness, does anyway! It doesn't immediately execute the O(n) algorithm, but just "remembers" that you wanted a concatenated list, until you actually access its elements. But that doesn't save you from paying the cost in the end. Because even though in the beginning the reference was cheap (in O(1), just like you wanted), when you do access the actual list elements, every instance of the ++ operator adds a little overhead (the cost of "interpreting the annotation" that you added to your reference) to the access of every element in the first part of the concatenation, effectively adding the O(n) cost finally.

Why is List.length linear in complexity?

I understand that lists are implemented as singly linked so they don't really have a constant structure that you can pin a length on, but each node should know how many nodes till the last element right? There isn't a way to add a node to some existing list and for that node not to be able to determine the length of the list it represents in constant time provided that the existing nodes already have that info.
I can understand why that wouldn't work in Haskell, for example, due to lazyness, but as far as I know F# lists aren't lazy. So, is the problem just in the extra memory overhead?
Seems to me like typical memory vs time performance consideration.
If standard f# list had the implementation You suggest, then it would need much more place in memory (consider one million long list of bools). And everyone using such list would have to deal with it. There would be no simple way to opt out of this other than writing completely new implementation of list.
On the other hand, it seems to be fairly simple to create a new type that would store length of succeeding list with each element basing on F# List. You can implement it on Your own if You need it. Those, who don't need it will use standard implementation.
I don't often find myself needing to know the length of the list, it's not like you need it to exit a for loop like you would with arrays in imperative languages.
For those rare cases when you really need to know the length asap, you can go with Carsten König's suggestion from a comment and make your 'a list into a ('a * int) list, where each node keeps the length of the tail as a tuple element.
Then you can have something like this:
let push lst e =
match lst with
| (_, c)::_ -> (e, c + 1) :: lst
| [] -> [e, 0]
and length and pop functions to match.
For all the other cases I'd call it a premature optimization.

Beginning of loop in doubly linked list?

One question I came across,
In a circular linked list, find node at the beginning of the loop?
EXAMPLE Input: A -> B -> C -> D -> E -> C [the same C as earlier] Output: C
Can one of the solutions be, to see if the address of value stored at these nodes are same?
So something like &(A->value) would return us address and then we find if an address is repeating, if yes that is the beginning of the loop?
You could do so, but this is very not efficient, in terms of space complexity, since you will need to store all nodes you 'saw along the way'.
A better solution would probably be Floyd's cycle finding algorithm, as described in this post

What is the cleanest way to extend this design for tree-rewriting?

I have a tree of objects, where each object has a std::vector of pointers to its children. The class of the object has a rewrite() method that is recursively applied to alter the object and its children, typically with the following transformations, where [N] is the object being rewritten and (M) is the element that rewrite() returns:
(A)
[A] / \ [A]
/ \ --> B X | --> (B)
B C \ B
C
What's the cleanest way of extending this setup to allow transformations like this?
A X
| / \
[B] --> C A
| |
C (Y)
That is, those that re-root the tree, move an element, insert a new element, and return the inserted element. I'm having a hard time coming up with something nice that also involves minimal refactoring. Thoughts?
Looks like, for speed, you'll need a "back-pointer" from each child node to its parent node. This way you can follow the chain of parent pointers given a pointer to any node all the way to the root, if that's what you need (I'm not sure how else you planned to find the root given just a pointer to an inner node, without backpointers?). Of course, you'll need to adjust the back pointer, as well as the "children" std::vector, whenever you rearrange the tree -- an unfortunate consequence of the slight duplication of information (no more than, say, a doubly linked list presents;-), but a small price to pay for the ease and speed of such bidirectional navigation.