I was asked an interesting computer science question today which left me stumped. The basic problem seems simple enough but it's more complex than at first glance. The idea is you have singly linked list that can either be standard linked list, a circular linked list (starts an ends in the same place), or a circuitous linked list (has a loop in it that doesn't necessarily go back to the start). How do you detect if a the linked list contains a loop without knowing the length of the linked list, and without being able to modify any of the data.
My initial thought was to just check and see if any of the nodes have null as their "next" value but this is an obvious pitfall because if it has a loop the code would run forever.
My next thought was to keep track of the memory location of each node and check to see if the next node's memory location has already appeared in the list, but this is not elegant, and could take a lot of memory (think of a long linked list).
After that I thought that at each position you could check the memory locations of each node up to the current one and compare them to the "next" node's memory location. This is essentially the same as my second idea but without having to store every node's memory location. This isn't a good solution either because it requires too many traversals of the list (aka SLOW!).
Next up I thought you could reverse the list and see if the the end node of the reverse list is the start node of the original list. This is good but it requires either of the two options:
a. Modifying the list (not allowed)
b. Making a copy of the list but in reverse order
Technically a could be remedied by then re-reversing the list but still then you'd have to go through the whole process twice.
Finally I had to ask for the answer and here is what I was told. Create two node objects one that is going to traverse the list quickly and one that is going to traverse the list slowly. The quick traverser moves two nodes ahead for every one node that the slow one moves. If the quick traverser ever catches the slow traverser then their must be a circuit. Why didn't I think of that? Such a simple solution.
Later I found a second answer that is pretty cool too. This one basically says always have one node to check held in memory. Basically start off with it being the second node and then check four more nodes to see if it gets selected again. If it doesn't then set the fourth node after it to the check node and check 8 nodes after that to see if any of them are the same. If they aren't set the eight node after the last check node to the new check node and then check 16 nodes after that to see if any of them are the same as the check node, and keep going until eventually one of the nodes that is traversed over is the same node as the check node, or a node with a "next" value of null is reached. I thought this solution was particularly interesting because it only requires one traverser.
I guess that's all I have on linked lists for now.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment