目录
前言:
环形链表是一种特殊的数据结构,其尾结点的next指针指向其链表中其他节点,形成一个闭环,其next指针也可以指向自己。
那么问题来了:由于尾结点的next指针不为NULL,我们无法用node->next==NULL为判断条件来遍历链表(会造成死循环)
而由于我现在的知识非常浅薄,目前使用快慢指针是现唯一方法,之后学习了c++等知识后,会有更多的解法。
1.我们如何判断这个链表是带环的?
我们可以得出以下结论
1、首先让快慢指针fast,slow指向链表的头节点Head。
2、fast一次走两步,slow指针一次走一步。
3、判断fast和slow是否移动到同一个节点上,如果移动到同一个节点上,就返回true
如果还没有移动到同一个节点上就继续往下走。
4、我们可以以(fast && fast ->next)作为循环的判断条件。
至此我们可以写出以下代码(◦˙▽˙◦)
bool hascycle(struct ListNode *head)
{
struct ListNode* slow = head, *fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
return true;
}
return false;
}
快指针fast一次移动两个节点,慢指针slow一次移动一个节点时,两者一定会相遇。
但是我们如何证明一定会相遇?
如果快指针走三步,四步......n步怎么办?
这才是重点
解析(●'◡'●):
当slow走一步,fast走三步的情况:
假设slow指针进环时,fast指针与slow之间的距离为N
设环的大小为C
slow走距离是:L
fast 走距离是:L+x*C +C-N
slow进环时,假设fast已经在环里面转了x圈
fast走的距离是slow的3倍
3*L=L+x*C + C-N
即:2*L= (x+1)*C-N
N是奇数时,C也是奇数
偶数=(x+1)*偶数–奇数
N是偶数时,C也是偶数
同时存在N是奇数且C是偶数,那么就永远追不上
总结一下:
1、N是偶数,第一轮就追上了
2、N是奇数,第一轮追击会错过,两指针之间距离变成C-1
a、如果C-1是偶数,下一轮就追上了
b、如果C-1是奇数,那么就永远追不上
同时存在N是奇数且C是偶数,那么就永远追不上
2.我们如何找到环的入口?
代码:
struct ListNode* detectCycle(struct ListNode* head) {
struct ListNode *slow = head, *fast = head;
struct ListNode* meet = NULL;
if (head == NULL)
return false;
while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if (slow == fast)
{
meet = fast;
while (meet != head)
{
meet = meet->next;
head = head->next;
}
return meet;
}
}
return NULL;
}
解释:
1、用两个指针head,meet分别指向链表的头节点和快慢指针相遇的节点;
2、同时移动两个指针
3、当两个指针指向同一个节点时,该节点就是环的入口点