判断单链表是否存在环及寻找环的入口点

一、判断单链表是否存在环

这个问题有很多方法,最容易想到的就是记录每个节点记录的次数。这里也介绍的是另一种简单而常见的方法

快慢指针法:

定义两个指针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;
}

 

原编辑时间 2018-09-08 23:33  

posted @ 2018-09-08 23:33  Rogn  阅读(1724)  评论(0编辑  收藏  举报