给定一个单链表,它可能包含环,也可能不包含环。环可能从节点头开始,也可能不是。计算单链表的节点数。
通过floyd循环检测算法找到环头节点。节点总数为环中的节点数加上不在环中的节点数。
如果链表不包含环,环中的节点数为0.
1.维护两个指针,hare(兔子)和tortoise(乌龟)
2.hare一次移动两步,乌龟一次移动一步,直到两个指针相等,或者hare指针指向NULL。
3.如果两个指向相等,将tortoise指针指向链表头。一步一步移动hare和tortoise指针,二者相等时,指向的节点为环的首节点。计算不在环上的节点个数。(也可以从将tortoise指针指向链表头时开始计算) 然后,从环的首节点开始计算环上节点的个数。
简单证明:
既有环又有非环部分:
假设非环部分长m,相遇点距离环的开始节点为k,环的长度为n,hare和tortoise在 i 时刻相遇,由于hare和tortoise每一时间各走2步和1步。
那么tortoise 走的距离:
i*1 = m+k+p*n。
那么hare走的距离:
i*2 = m+k+q*n
有上面两个式子可以得到:m=(q-2*p)n-k
所以当从相遇点开始,hare走(q-2*p)n距离再回退k个距离 和 tortoise从head开始走m个距离时 即可再次相遇,相遇点在环的首节点。
如果单链表只有环,即环从head开始,hare和tortoise相遇时有 i mod n == 2i mod n 所以i mod n == 0,所以相遇点在head,那么tortoise从head开始,hare从相遇点开始(即head),他们再次相遇时还是在head。也就是环的首节点。
综上,如果有环 hare和tortoise再次相遇一定在环的首节点。
当链表中包含环时,找到相遇的节点
LinkedListNode FindBeginning(LinkedListNode head) {
LinkedListNode n1 = head;
LinkedListNode n2 = head;
// Find meeting point
while (n2.next != null) {
n1 = n1.next;
n2 = n2.next.next;
if (n1 == n2) {
break;
}
}
// Error check - there is no meeting point, and therefore no loop
if (n2.next == null) {
return null;
}
/* Move n1 to Head. Keep n2 at Meeting Point. Each are k steps
/* from the Loop Start. If they move at the same pace, they must
* meet at Loop Start. */
n1 = head;
while (n1 != n2) {
n1 = n1.next;
n2 = n2.next;
}
// Now n2 points to the start of the loop.
return n2;
}