Chapter 2 Linked Lists

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.
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.;