判断单链表是否存在环及寻找环的入口点
一、判断单链表是否存在环
这个问题有很多方法,最容易想到的就是记录每个节点记录的次数。这里也介绍的是另一种简单而常见的方法
快慢指针法:
定义两个指针slow, fast。slow指针一次走1个结点,fast指针一次走2个结点。如果链表中有环,那么慢指针一定会再某一个时刻追上快指针(slow == fast)。如果没有环,则快指针会第一个走到NULL
int has_cycle(node *head) {
if (head == NULL) return false;
node* fast = head;
node* slow = head;
while (1)
{
if (slow->next != NULL) slow = slow->next; //慢指针走一步
else return false;
if (fast->next != NULL && fast->next->next != NULL) fast = fast->next->next; //快指针走两步
else return false;
if (slow == fast) return true;
}
}
二、寻找环的入口点
其中一个回到起点:
当fast按照每次2步,slow每次一步的方式走,发现fast和slow重合,确定了单向链表有环路。接下来,让fast回到链表的头部,重新走,每次步长1,那么当fast和slow再次相遇的时候,就是环路的入口了。
证明:
在fast和slow第一次相遇的时候,假定slow走了n步,环路的入口是在p步,那么
slow走的路径: p+c = n;(1) c为fast和slow相交点 距离环路入口的距离
fast走的路径: p+c+k*L = 2*n;(2), L为环路的周长,k是整数
fast从头开始走,步长为1.
经过n步,fast和slow都会到达p + c这一点。将(2)-(1)得k*L = n,说明n是L的倍数,同时p + c = n,
所以fast和slow都走p步时,fast距(p + c)差c,slow还差c回到(p + c),所以p是他们的第一个交点,之后的轨迹就一模一样了。
另一个证明:
假设第一次相遇的时间是k,则慢指针走了k步,快指针走了2k步,2k-k=k=t*L,即t圈
fast回到起点,走k-m才能到达,slow走L-m就能到达,因为k是L的倍数,所以slow可能会转(t-1)圈等fast的到来,然后也是在交点相遇
node* find_loopport(node * head)
{
node* fast = head;
node* slow = head;
//两个互指不考虑
//判断是否存在环,如果存在得到相遇位置
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if (fast == slow) break;
}
//fast到达NULL,表示不存在环
if (fast == NULL || fast->next == NULL) return NULL;
//将一个指针移到开始处,步长都变成一
fast = head;
while (slow != fast)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}
个性签名:时间会解决一切