
Linked List Problems (2002) [pdf] - starschema
http://cslibrary.stanford.edu/105/LinkedListProblems.pdf
======
herodotus
One of my all-time favourites is this: suppose you have a linked list that
eventually cycles: that is, the link of one of the nodes in the list points to
a previous node in the list, but not necessarily the first node. Write a
function to compute the cycle length of the cycle. Now if it was just a simple
circular list, this would be trivial: set a pointer to a node, and move
another pointer a node at a time until the two pointers meet. However, this
list has an initial sequence of nodes of unknown length that is not in the
cycle. So the trick is really to find a node that is guaranteed to be in the
cycle.

A nice way to do this is to start with two pointers to the head to the list,
and then advance one by a single node at a time, and the other by two nodes at
a time. It is easy to see that they will eventually meet on the cycle. Once
that happens, it is easy to count the cycle length.

Now suppose you are running a casino and you are using a random number
generator. For any given seed, the generator will eventually cycle, but not
necessarily to the seed. You can compute the cycle lengths for a given seed
with two variables by analogy with the linked list solution stated above.

~~~
greenleafjacob
As others have said, this is actually a, perhaps the best, canonical example
of a terrible interview question [1].

[1]
[https://news.ycombinator.com/item?id=7953725](https://news.ycombinator.com/item?id=7953725)

~~~
abalone
I mostly agree with this, _however_ if the job requires a C.S. degree or
equivalent and you assume all qualified candidates have been exposed to this,
then perhaps it can be a useful question for testing how well you can explain
a solution.

Just look at how varied and complicated some of the explanations are for this
out there. Some take mathematical approaches, others (me) try to boil it down
to a few intuitive sentences... I can see it giving some insight into a
candidate.

~~~
marcus_chang
Unless you're interviewing Dijkstra, it's a ridiculous question to ask. Anyone
who's heard it before knows the answer instantly (I knew the answer before I
even finished reading the description) and anyone unfamiliar with it will
likely flail.

~~~
abalone
Nah, I wouldn't say everyone would flail but Dijkstra. There are simpler
solutions to cycle detection.. maybe not as _efficient_ as Floyd, but you
could answer the question. For example you could start with something basic
and unoptimized like keep a data structure of all the nodes you walk, and when
you hit one that's in the structure then you've encountered a cycle. This is a
fair approach to engineering solutions to novel problems -- don't panic, get
something working, then optimize where necessary.

And if you already know the best practice, like I said, it's not just about
whether you know the answer -- it's how simply you can explain it to a
teammate and the theory behind why it works. That's also a useful skill to
interview for, especially for more experienced candidates.

------
dgquintas
"Just say no to linked lists!"
[https://youtu.be/fHNmRkzxHWs?t=2099](https://youtu.be/fHNmRkzxHWs?t=2099)

~~~
avodonosov
He says because of cache misses due to each element being allocated
individually.

But then working with references is bad in general? If your vector stores
references to objects it's bad? Your object fields point to strings - bad too.

Should we ditch Lisp and Java?

Note, data should not be continuous to stay in cache. Cache is more like paged
virtual memory, it consists of cache lines.

Before subscribing to the "never use linked lists" view I would like to see a
nontrivial program rewritten from lists and references to the "allocate
everything at once" approach and measure the performance.

If you want to avoid references and allocate everything in place, it may force
your program to do a lot of copying.

Also, not only CPU performance matters - programmer performance is important
too. So-called "high-level languages" try to optimise this.

~~~
SunnySkies
You're misunderstanding the advice about contiguous. It's not that it's more
likely to stay in cache, but if you're accessing data sequentially it's more
likely the data you're going to access next is already in cache.

Most (all I've read/looked at) benchmarks in Java have data structures backed
by linked lists utterly smashed by things implemented by an array.

There was in the last year or two a good c++ talk where a game or game engine
changed their storage to be more array like and got roughly a 30% boost in
performance. Memory locality is usually king, which is why linked lists are
rarely used.

~~~
avodonosov
> if you're accessing data sequentially it's more likely the data you're going
> to access next is already in cache

Why?

~~~
yorwba
Cache prefetching.
([https://en.wikipedia.org/wiki/Cache_prefetching](https://en.wikipedia.org/wiki/Cache_prefetching))

Essentially, many workloads access data sequentially and therefore modern
cache architectures have special optimizations to make these memory accesses
as fast as possible, by prefetching the next item in the sequence before it is
actually needed.

~~~
avodonosov
Ah, yes.

Well, anyways I value code simplicity more than anything.

Maybe CPUs will learn to do cache prefetch for link oriented languages: if a
"load and dereference" pattern is detected, CPU could prefetch the data
referenced by pointers it has in registers or recently fetched cache line.

BTW, linked list elements are not necessary located far away from each other,
if we allocated them one after another chance are they are near each other in
memory.

------
xfs
> 7\. Build — Local References
    
    
        struct node* BuildWithLocalRef() {
          struct node* head = NULL;
          struct node** lastPtrRef= &head; // Start out pointing to the head pointer
          int i;
          for (i=1; i<6; i++) {
            Push(lastPtrRef, i); // Add node at the last pointer in the list
            lastPtrRef= &((*lastPtrRef)->next); // Advance to point to the new last pointer
          }
          // head == {1, 2, 3, 4, 5};
          return(head);
        }
    

> This technique is short, but the inside of the loop is scary. This technique
> is rarely used, but it's a good way to see if you really understand
> pointers.

> This technique is never required to solve a linked list problem, but it will
> be one of the alternative solutions presented for some of the advanced
> problems. The code is shorter this way, but the performance is probably not
> any better.

This is what Linus Torvalds called "good taste" code and used in Linux kernel.
There was some argument about it though:
[https://news.ycombinator.com/item?id=5030845](https://news.ycombinator.com/item?id=5030845).

(Incidentally, I found Nginx is written in the same style recently as I read
it - manual linked list manipulation with this double pointer technique
everywhere..)

------
throwaway7645
I work with traditional RDBMS & and old hierarchical linked-list DB. The
linked-list has great performance with super speedy Fortran, but it is a major
pain to retrieve any information. Having to write complicated pointer commands
is tedious at best. Select * is such a luxury.

------
abhimt
For other data structures, refer [http://www.techiedelight.com/list-of-
problems/](http://www.techiedelight.com/list-of-problems/)

------
darepublic
I remember solving these in my spare time while bored at my first internship
as a developer. Good memories

------
doiwin
I have been coding for decades now and never had a use case for a linked list.

~~~
AzzieElbab
I use lists daily. Why not? It is a stack or queue, if you reverse it

------
lucio
What is LISP

~~~
delta1
In case you're serious, and too lazy to google it... [1]

LISP is a family of programming languages.

[1]
[https://www.google.com/search?q=LISP](https://www.google.com/search?q=LISP)

~~~
lucio
It was sort of a Jeopardy response. I'm sorry if it was too opaque. The OP's
article just brought LISP into my mind.

