链表——带环链表

带环链表是单向链表里一种,理解如下图

注意区分环形链表(即首尾相接)

作为单向链表你常见题型之一的带环链表,考点有难易两种:

  1. 给出一个链表判断链表是否带环

譬如力扣题库141题(链接:https://leetcode.cn/problems/linked-list-cycle/

这题较简单,通过快慢指针的做法就能轻松解决,即定义两个结构体指针,一个走一步,一个走两步,当两指针相等时判断为带环,若遍历完整个链表不存在相等情况,就说明该链表不带环,附上代码

bool hasCycle(struct ListNode *head) {
    while(head == NULL || head->next == NULL)
    {
        return false;
    }
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next)//只判断fast或者fsat->next可能会出现空指针
    {
        fast=fast->next->next;
        slow=slow->next;
        if(fast==slow)//两指针相遇
        {
            return true;
        }
    }
    return false;
}

从图形上理解就是(以带环为前提),定义了两个结构体指针,

两指针同时从起点(头节点)出发,但是fast相比slow多走了一步,也就是说会先到达环处,

当slow到达环时,假设存在(也有可能slow刚好入环,fast刚好到达入环点):

两指针会在环内一直运动,犹豫fast速度快于slow,这事就可以理解成追击问题,当fast追上slow时(即slow==fast时),循环中止,判断该链表带环。

在这个地方还存在一些问题:

  1. fast一定能追上slow吗?

  1. fast追slow一定不会错过吗?

  1. 若slow走一步,fast走三步还能追上吗?

  1. 错过一定追不上吗?

假设环内的节点有C个(可以抽象成环的周长为C),slow入环时与fast相距X个节点(在运动方向上相距X)

问题1:

由于fast的速度为2,slow为1。fast相对于slow的速度为1,那么在环内slow与fast的距离会由C-X=>C-X-1=>C-X-2=>...=>C-(C+1)=>0,当相距为0时,slow与fast相遇。因为C,X一定是整数,因而一定能追上。

问题2:

根据问题1的解答可知,因为C,X为整数,距离每次减小1,那么当距离为0时,就会追上且不会错过。

问题3:

根据问题1,fast相对于slow的速度为2,即每运动一次,二者间距离缩小2,对于能否相遇存在几种情况:若C-X为奇数,那么有C-X=C-X-2=>...=>C-(C-1-2)=>1-2=>-1,-1意味着fast跳过了slow,到了slow的前面,即二者错过。若C-X为偶数那么C-X=C-X-2=>...=>C-(C-2)=>2-2=>0,即fast追上slow并相遇。根据分析很容易想到,二者间能否相遇,会不会错过与二者各自的速度没有关系,其取决于二者间的相对速度。

问题4:

根据问题3,当fast超过slow后,设超过的距离为n,那么此时fast与slow相距C-n,通过分析C-n与相对速度的关系(如:奇偶、C-n与相对速度的倍数关系)就可以得出答案。

  1. 在1的基础上,返回入环节点

譬如力扣题库142题(链接:https://leetcode.cn/problems/linked-list-cycle-ii/

(此解析情况下fast走两步,slow走一步)

设:head到入环点的距离为L,slow与fast相遇时相距入环点的距离为X,环的长度为C,如图:

那么就可以表示出两指针的运动距离:

(nC表示在slow入环前,fast已经在环内运动了n圈。有人可能会好奇:为什么slow不能运动很多圈呢?——很简单,当slow入环时若fast刚好到达入环点那么取得最小相距0;当slow入环时,若fast为入环点+1取得最大相距距离C-1,犹豫fast快于slow,每次运动会让相距距离减一,因而slow最多只能在环内运动C-1,并不能到达一圈)

又fast的速度为slow的两倍,并且二者同时从起点出发,则有

化简得

便于理解可以将式子化成

根据前面的式子+等式可以知道,(n-1)C+C-X的位置正好为入环点,因此有了一个结论:

一个指针从相遇点走,另一个指针从起点走,二者会在入环点相遇(两指针一次都只走一步)。

附上代码

struct ListNode *detectCycle(struct ListNode *head) {
    if(head==NULL || head->next==NULL)
    {
        return NULL;
    }
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    while(fast && fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
        if(fast==slow)  //L=nC-X
        {
//仅仅在判断是否有换的基础上多了一个遍历
//创建一个cur指针从起始位置开始移动
//slow继续移动
            struct ListNode* cur = head;
            while(cur!=slow)
            {
                cur=cur->next;
                slow=slow->next;
            }
            return cur;
        }
    }
    return NULL;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值