寻找带环链表的环节点

一:判断链表是否带环

思路:

用快慢指针从头结点开始向前走,slow每向前走一步,fast向前走两步。如果最终fast和slow相遇,就说明链表带环;如果最终fast或fast->next为NULL指针,链表就不带环。

原理:

  • 链表不带环:fast会先到达尾结点,结束。

  • 链表带环:fast比slow较早进环,当slow到达环结点,fast有两种情况:

  • fast刚好绕环n(n>=1)圈后,走到环结点,slow和fast相遇

  • 当slow走到环结点时,fast在环内,假设fast和slow的距离为n,fast和slow继续向前走,每走一次,fast和slow的距离-1,n次后fast和slow相遇

思考:

如果slow每向前走一步,fast向前走m步,快慢指针会不会相遇?

答案是不一定,以m=3为例:

设fast与slow相距n,环的周长是c,当slow到达环节点后,由于fast与slow的相对速度是2

对n分类讨论:

  1. n是偶数,继续向前走,刚好可以相遇

  1. n是奇数,继续向前走,当fast与slow相距1时,二者再向前走一次,fast会和slow”擦肩而过“。这时,fast与slow相距c-1,又要对c-1分情况讨论:

  • c-1是偶数,继续向前走,可以相遇

  • c-1是奇数,fast和slow又会“擦肩而过”,而且之后快慢指针错过后,二者的距离都是c-1,它们永远不会相遇

代码:

struct ListNode {
    int val;
    struct ListNode* next;
};

bool hasCycle(struct ListNode* head) 
{
    struct ListNode* fast = head; 
    struct ListNode* slow = head;
    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast)
        {
            return true;
        }
    }
    return false;
}

二:找相遇结点的指针和头指针的相遇结点

思路:

分别用两个指针存储头节点和快慢指针的相遇节点,让它们同时向前走,最终相遇的结点就是链表环结点

原理:

设头结点与环结点距离为L,slow和fast相遇时,slow与环结点距离为x,环的长度是c,则当slow与fast相遇时:

  1. slow走的距离是:L+x

问题:slow可能在环内走一圈吗?

不可能,因为在slow进环的时候,fast与slow的距离最大是c-1,slow最多走c-1次就可以与fast相遇,此时x=c-1

  1. fast走的距离是:L+nc+x

n表示在fast与slow相遇前,fast在环内走的圈数

对n的理解:当L远大于c时,在slow没入环前,fast会在环内走很多圈,n将是一个很大的数;当c远大于L时,slow入环前,fast在环内一圈也没走,之后fast追slow会走一圈,所以n = 1。

  1. 根据快慢指针的关系,fast走的距离是slow的2倍,得出下面关系式:

L = (n-1)c + (c-x)

  1. c-x是相遇结点到环结点的距离,由关系式得出:slow与fast的相遇结点,与头节点同时向前走,一定会相遇

理解:当头结点走L距离也就是到达环结点时,相遇结点的指针刚好绕环走了n-1圈回到相遇点,并继续走了c-x,也到达了环结点,与头节点相遇

代码:

struct ListNode
{
    int val;
    struct ListNode* next;
};

struct ListNode* detectCycle(struct ListNode* head) {
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while (fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast)
        {
            struct ListNode* meet = fast;
            struct ListNode* first = head;
            while (meet != first)
            {
                meet = meet->next;
                first = first->next;
            }
            return meet;
        }
    }
    return NULL;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值