Problem 2.5: Given a circular linked list, implment an algorithm which returns node at the beginning of the loop.
DEFINITION
Circular linked list: A (corrupt) linked list in which a node's next pointer points to an earlier node, so as to make a loop in the linked list.
EXAMPLE
input: A->B->C->D->E->C[the same C as earlier]
output: C
The only solution I can come up with is the most intuitive one, marking visited node with a hash table.
However, when I turned to the answer page, I found a solution which is so tricky that it takes several minutes to understand:
Let me explain the code above in detail:
First of all, there are two pointers pointing to the head of linked list. We move pointer p1 one step each time, while moving p2 two steps each time. Let the number of steps from head to the start of loop be k. Then, when p1 comes to the start of the loop in linked list, p2 is k (which is unknown) steps ahead in the loop.
Let the number of nodes in the loop is n. If we keep moving p1 by one step each time and moving p2 by two steps, it will take (n-k) steps before p2 meet p1. Now, let us calculate how many steps is needed to move p1 to the start of loop: it is n-(n-k)=k.
Although k is now known, we can measure it in another way: let a pointer p3 move by one step each time from the head of linked list, while moving p1 at the same speed. When p1 and p3 meet, they are at the start of loop.
What a tricky solution! I learned that we can not only use more than onepointers, but also moving different pointers at different speed.
DEFINITION
Circular linked list: A (corrupt) linked list in which a node's next pointer points to an earlier node, so as to make a loop in the linked list.
EXAMPLE
input: A->B->C->D->E->C[the same C as earlier]
output: C
The only solution I can come up with is the most intuitive one, marking visited node with a hash table.
class node:
def __init__(self, data=None):
self.data = data
self.next = None
def find_beginning(head):
hash_table = {}
while head != None:
if hash_table.has_key(head.data):
return head
else:
hash_table[head.data] = 1
head = head.next
return False
if __name__ == "__main__":
n1 = node(1)
n2 = node(2)
n3 = node(3)
n4 = node(4)
n5 = node(5)
n1.next = n2
n2.next = n3
n3.next = n4
n4.next = n5
n5.next = n3
print find_beginning(n1).data
However, when I turned to the answer page, I found a solution which is so tricky that it takes several minutes to understand:
def find_beginning(head):
p1 = head.next
p2 = head.next.next
while p1.data != p2.data:
p1 = p1.next
p2 = p2.next.next
p3 = head
while p1.data != p3.data:
p1 = p1.next
p3 = p3.next
return p3
Let me explain the code above in detail:
First of all, there are two pointers pointing to the head of linked list. We move pointer p1 one step each time, while moving p2 two steps each time. Let the number of steps from head to the start of loop be k. Then, when p1 comes to the start of the loop in linked list, p2 is k (which is unknown) steps ahead in the loop.
Let the number of nodes in the loop is n. If we keep moving p1 by one step each time and moving p2 by two steps, it will take (n-k) steps before p2 meet p1. Now, let us calculate how many steps is needed to move p1 to the start of loop: it is n-(n-k)=k.
Although k is now known, we can measure it in another way: let a pointer p3 move by one step each time from the head of linked list, while moving p1 at the same speed. When p1 and p3 meet, they are at the start of loop.
What a tricky solution! I learned that we can not only use more than onepointers, but also moving different pointers at different speed.