环形链表
题目要求:
思路: 假想「乌龟」和「兔子」在链表上移动,「兔子」跑得快,「乌龟」跑得慢。当「乌龟」和「兔子」从链表上的同一个节点开始移动时,如果该链表中没有环,那么「兔子」将一直处于「乌龟」的前方;如果该链表中有环,那么「兔子」会先于「乌龟」进入环,并且一直在环内移动。等到「乌龟」进入环时,由于「兔子」的速度快,它一定会在某个时刻与乌龟相遇,即套了「乌龟」若干圈。
我们可以根据上述思路来解决本题。具体地,我们定义两个指针,一快一满。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head
,而快指针在位置 head->next
。这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。
为什么快指针每次走两步,而不是三步或者更多的步数,接下来进行说明:
实现代码:
bool hasCycle(struct ListNode *head) {
struct ListNode *slow = head;
struct ListNode *fast = head;
while(fast&&fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
return true;
}
return false;
}
环形链表二
题目要求:
思路: 基于上个题目的思路,
我们使用两个指针,fast
与 slow
。它们起始都位于链表的头部。随后,slow
指针每次向后移动一个位置,而 fast
指针向后移动两个位置。如果链表中存在环,则 fast
指针最终将再次与 slow
指针在环中相遇。
如图所示:
有了 a=c+(n−1)(b+c)的等量关系,我们会发现:从相遇点到入环点的距离加上 n−1圈的环长,恰好等于从链表头部到入环点的距离。
因此,当发现 slow
与 fast
相遇时,我们再额外使用一个指针 ptr
。起始,它指向链表头部;随后,它和 slow
每次向后移动一个位置。最终,它们会在入环点相遇。
代码实现:
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *slow = head;
struct ListNode *fast = head;
while(fast&&fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
{
struct ListNode *pre = head;
while(slow != pre)
{
slow = slow->next;
pre = pre->next;
}
return slow;
}
}
return NULL;
}