【链表】
Q:Given a circular linked list, implement an algorithm which returns node at the begin-ning 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
题目:写一个算法求出有环单链表中环的起始点。
举例:
输入:A -> B -> C -> D -> E -> C
输出:C(环的入口节点)
解答:
针对这个问题,一个前置问题是:判断一个链表是否有环?这个问题有快慢指针法、反转链表法、hash法等。
如果我们已知一个链表有环结构存在,我们如何找出环的入口节点呢?
同样可以使用快慢指针的方法,快指针fast每次走2步,慢指针slow每次走一步,当fast和slow相遇时,我们确定了单向链表有环路。接下来,让fast回到链表的头部,重新走,但这次让fast和slow以同样的速度走,每次走1步。那么当fast和slow再次相遇的时候,就是环路的入口了。为什么???
证明:在fast和slow第一次相遇的时,假定slow走了n步,环路的入口是在p步,那么:
slow走的路径: p+c = n; c为fast和slow相交点 距离环路入口的距离
fast走的路径: p+c+k*L = 2*n; L为环路的周长,k是整数。因为fast比slow多走的路就是它多绕了几个圈。
显然,如果从p+c点开始,slow再走n步的话,还可以回到p+c这个点。
同时,fast从头开始走,步长为1,经过n步,也会达到p+c这点。
显然,在这个过程中fast和slow只有前p步骤走的路径不同。所以当p1和p2再次重合的时候,必然是在链表的环路入口点上。
有了思路以后,我们可以编码如下(C代码):
NODE* find_loop(NODE* list) { if(list==NULL) return NULL; NODE *fast=list,*slow=list; while(fast->next!=NULL){ fast=fast->next->next; slow=slow->next; if(fast==slow) break; } if(fast->next==NULL)//如果fast走到尽头了,说明没有环结构存在 return NULL; slow=list; while(fast!=slow){//再次相遇时,在环入口处 fast=fast->next; slow=slow->next; } return fast; }
JAVA参考代码:
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;
}
作者:Viidiot 微信公众号:linux-code