Tech Support > Computers & Technology > Programming > linked list
linked list
Posted by Ben Pfaff on April 24th, 2006


"jamihuq" <jamihuq2002@yahoo.com> writes:

One way to do this is to go through the linked list counting the
number of elements, then go back through it again and return the
5th from the end. That takes two passes.

The interview question is asking you to find a way to do so
without making the second pass. I can think of two ways,
off-hand; one requires modifying the list, the other does not.
--
int main(void){char p[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv wxyz.\
\n",*q="kl BIcNBFr.NKEzjwCIxNJC";int i=sizeof p/2;char *strchr();int putchar(\
);while(*q){i+=strchr(p,*q++)-p;if(i>=(int)sizeof p)i-=sizeof p-1;putchar(p[i]\
);}return 0;}

Posted by kp on April 25th, 2006


Ben Pfaff wrote:
Try to maintain two pointers at the head of the linked list (say x and
y). Now move x, node by node of the list. When you have moved x by 5
nodes, start moving x and y simultaneoulsy. When x reaches the end of
the list, y will be exactly 5 nodes behind then end. This is just one
pass.

--kp


Posted by Steve O'Hara-Smith on April 25th, 2006


On 24 Apr 2006 20:07:07 -0700
"kp" <kp.discuss@gmail.com> wrote:

It is two passes - they are just being done at the same time.
One single pass algorithm would be to use a five element circular buffer
or similar five element store (use it to hold the pointers)a`1 .
This is only a real benefit if following the links is more expensive than
manipulating the store pointer.

--
C:>WIN | Directable Mirror Arrays
The computer obeys and wins. | A better way to focus the sun
You lose and Bill collects. | licences available see
| http://www.sohara.org/

Posted by Logan Shaw on April 26th, 2006


Steve O'Hara-Smith wrote:
That brings up an interesting point. What really constitutes a pass, and
why do we care about how many passes?

If it's only about being sure you don't access the *original* data in
its original location, then the queue method will work fine. But then,
so will my algorithm that I hereby propose as an alternative:

Step 1: make a copy of the list; this can easily be done in one pass.
Step 2: use any algorithm you like to find the 5th element from the end.

If there is something unsatisfying about the algorithm, I think it indicates
that the specification "in one pass" has failed to capture what is really
desired. If you want, you can take the make-a-copy-first approach and turn
any algorithm that merely reads a data structure into a one-pass algorithm.

So I think it's important to be specific about someone is really after when
asking for something that's one-pass. Perhaps the goal is to minimize the
times we hit main memory. After all, in today's processors, L1 and L2 cache
are many times faster than main memory, and linked lists are notorious for
being spread all over memory nearly at random, which means just about every
node is a cache miss. So, one motivation for doing it in one pass is to
avoid paying that penalty in the case where the whole linked list won't
fit in the cache. (If the whole linked list does fit, then every node is
a cache hit the second time around, so no big deal. In which case,
incidentally, the cache has effectively done a make-a-copy-first optimization
on your algorithm.)

Anyway, if the point is to avoid extra cache misses (or hits to disk), then
the two pointers, one which trails the other by 5 nodes, solves the problem
quite well, provided that the cache is large enough to hold 5 nodes in it,
which is a pretty fair bet.


Getting back to the original puzzle, another way to solve this is to use
a recursive function: you can build up a list of addresses on the stack,
and when you hit the end, you merely return a number which indicates the
length of the sublist that you saw. Building it up, you could first start
with a recursive function to find the length of a list:

int linked_list_length (Node *list)
{
if (list == NULL) { return 0; }
return (1 + linked_list_length (list->next));
}

Now, to find the pointer to the Kth node from the end, you simply
need to modify the above function to check if it ever sees a sublist
of the appropriate length:

int kth_from_end (int k, Node *list, Node **result)
{
int sublist_length =
(list == NULL)
? 0
: (1 + kth_from_end (k, list->next, result));

if (sublist_length == k) { *result = list; }

return sublist_length;
}

That will waste O(n) stack, but it has the advantage that it doesn't
have a bunch of special cases like an algorithm that first moves a
pointer past 5 nodes before it starts another pointer moving. It
really wouldn't need very many test cases.

Actually, come to think of it, the point of the original question in
the interview may have been not so much the algorithm, but realizing
what test cases would needed for it. When you have special cases,
you need to come up with the right list of test cases to cover all
the cases, and that's a skill in itself.

- Logan

Posted by al pacino on April 26th, 2006


kp wrote:
wow, what simple and elegant solution!
thanks


Posted by Steve O'Hara-Smith on April 26th, 2006


On Wed, 26 Apr 2006 00:19:06 GMT
Logan Shaw <lshaw-usenet@austin.rr.com> wrote:

Consider the possibility that the pointers in the linked list are
expensive to follow - eg: URLs - making it desirable to follow each pointer
only once if possible.

Not necessarily, nobody ever said the list was small enough to hold
in a single machine.

--
C:>WIN | Directable Mirror Arrays
The computer obeys and wins. | A better way to focus the sun
You lose and Bill collects. | licences available see
| http://www.sohara.org/


Similar Posts