views:

408

answers:

5

This seems to be returning the correct answer, but I'm not sure if this is really the best way to go about things. It seems like I'm visiting the first n nodes too many times. Any suggestions? Note that I have to do this with a singly linked list.

Node *findNodeFromLast( Node *head, int n )
{
    Node *currentNode;
    Node *behindCurrent;
    currentNode = head;
    for( int i = 0; i < n; i++ ) {
        if( currentNode->next ) {
            currentNode = currentNode->next;
        } else {
            return NULL;
        }
    }

    behindCurrent = head;
    while( currentNode->next ) {
        currentNode = currentNode->next;
        behindCurrent = behindCurrent->next;
    }

    return behindCurrent;
}
A: 

You could use a doubly linked list, which is a linked list that also stores the address of it's parent. Transversal is much easier, since you can start at the end and work your way to the beginning.

MeDiCS
Indeed, sorry I was unclear. This has to be done with a singly linked list.
Stephano
Is there an specific reason for that? I fail to see any problems arising from using it...
MeDiCS
Yea, it's homework :) . Stupid rules.
Stephano
Use an array of pointers, one for each cell. The only problem is the overhead of updating it...
MeDiCS
+2  A: 

Your running time is still O(n), so I don't see that there's any problem with it.

Conceptually, you can divide the list into two parts: the part before the node you're returning, and the part after. One of these parts will have to be walked twice. Your implementation has chosen the first, at the advantage of no extra memory use (other than a couple temporary variables).

Alternatively, you could create a stack, walk the list and put each element into the stack, and then pop off n items. Then you'd be walking the end of the list twice, instead of the beginning. This has the disadvantage of storing the list in memory twice. (You could make the stack a little smarter by only storing n elements and dropping them off the bottom of the stack as new ones are added; then you're only using enough space to store n Nodes.)

I'm assuming that you can't blow away the list by reversing it in place. Then it's constant memory, still O(n), still walking the end of the list twice.

Eric Warmenhoven
+4  A: 

Start an two pointers. Move the first one N elements ahead and then move each pointer 1 element. When the first pointer reaches the end, second pointer will give the answer.

EDIT : Yes, it is pretty much the same code as given in the question. But I feel the pseudo code make it clearer. To answer the question, there is no other go as first N elements have to be visited twice. If N is small it doesn't matter. If N is large then the second loop will be small. So it is always an O(n) solution.

fastcodejava
is that different to his current solution?
stmax
... which is obviously what the code in the original post implements (assuming it is done correctly)
AndreyT
Isn't this what the implementation in the question does?
Eric Warmenhoven
I agree that the alg must be O(n). I don't agree that "first N elements have to be visited twice". See my answer for an alg that visits all nodes exactly once.
Mark Byers
@Mark - I think that will work. Is there a name for that kind of trick? Upvoted the answer.
fastcodejava
I agree that this is a good algorithm for my supplied code. However, I was looking for a "suggestion", as in, ways to improve. Your edit improved things quite a bit :) . Cheers.
Stephano
+1  A: 

First count the number of nodes in the list. Then traverse again, counting off n less nodes. Still an O(n) algorithm, that's unavoidable.

Hans Passant
If there's any difference, I suspect this is worse than the questioner's code. The questioner has a chance of cache hits, if n is small enough that n nodes fit in (some level of) cache. With your algorithm, the whole list has to fit in cache in order to get any cache hits at all.
Steve Jessop
+4  A: 

Another way to do it without visiting nodes twice is as follows:

Create an empty array of size n, a pointer into this array starting at index 0, and start iterating from the beginning of the linked list. Every time you visit a node store it in the current index of the array and advance the array pointer. When you fill the array, wrap around and overwrite the elements you stored before. When you reach the end of the list, the pointer will be pointing at the element n from the end of the list.

But this also is just an O(n) algorithm. What you are currently doing is fine. I see no compelling reason to change it.

Mark Byers
Basically, a ring buffer of pointers.
Mike DeSimone
Nice alternative. While lots of these are good answers, I chose yours for being concise. Also, you gave me a suggestion, and not an algorithm of what I already did in code :).
Stephano
I think that will work. Is there a name for that kind of trick? Upvoted.
fastcodejava
@factcodejava: Mike is right - it's a ring buffer that stores the last N entries.
Mark Byers
Very beautiful solution. Two thumbs up
Pierreten