最近在刷LeetCode,遇到一个判断链表中是否有回路的题,也算是一道经典题目了。今天和大家分享一下解题方法。
基本思路还是使用Floyd判圈算法。指针hare每次向后移动2个节点,指针tortoise每次向后移动1个节点。如果hare最终指向尾节点则该链表无回路。否则,该链表有回路。(详见《LeetCode #141 Linked List Cycle》)
现在我们已经知道了链表有没有回路,但是如何在不使用额外空间并且不修改原链表的基础上获得回路的起始节点呢?这需要一些数学推导:
设链表起始节点为H,回路起始节点为S,两指针第一次相遇节点为M。
设回路的长度为c,S到M的距离为c1,M到S的距离为c2。
设H到S的距离为d。
设hare每次移动2,tortoise每次移动1。移动k次后两者相遇。不难发现,H到M的距离为k。
为了便于理解,请参考下图:
由已知条件,可得到下列等式:
hare和tortoise相遇时,hare套了tortoise若干圈。则有:
2k – k = nc = k,n为正整数。
从上图中定义的距离关系,可以得到
c = c1 + c2
k = d + c1
由以上格式变换可得: d = nc – c1 = c2 + (n-1)c
该式具有什么重要意义呢?假设2个指针分别从H和M出发,都是每次移动1的距离,他们会在哪里相遇?
一个指针从H出发移动d后到达M。另一指针移动 c2 + (n-1)c,(n-1)c意味着绕了n-1圈又回到M,实际上相当于走了c2的距离,到达M。我们发现,两指针必然在S相遇。
此时两指针相遇的节点即是回路的起始节点。
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
struct
ListNode *detectCycle( struct
ListNode *head) { if
(head == NULL) return
0; struct
ListNode *hare = head; struct
ListNode *tortoise = head; while
(hare->next != NULL && hare->next->next != NULL){ hare
= hare->next->next; tortoise
= tortoise->next; if
(hare == tortoise) { hare
= head; while
(hare != tortoise){ hare
= hare->next; tortoise
= tortoise->next; } return
hare; } } return
NULL; } |
注:原文见我的博客《LeetCode #142 Linked List Cycle II》
原文地址:http://blog.csdn.net/u014076884/article/details/46528733