C语言编程实战:每日一题:环形链表Ⅱ

编程达人挑战赛·第5期 10w+人浏览 129人参与

欢迎来到 s a y − f a l l 的文章 欢迎来到say-fall的文章 欢迎来到sayfall的文章

在这里插入图片描述


🌈这里是say-fall分享,感兴趣欢迎三连与评论区留言
🔥专栏: 《C语言从零开始到精通》 《C语言编程实战》 《数据结构与算法》 《小游戏与项目》
💪格言:做好你自己,你才能吸引更多人,并与他们共赢,这才是你最好的成长方式。



题目:环形链表Ⅱ

一、思路解析

基于 Floyd判圈算法(龟兔赛跑算法) ,核心目标是 不仅检测链表是否有环,还能找到环的入口节点。算法分为「环检测」和「入口定位」两个关键阶段

1. 核心原理铺垫(数学推导)

假设链表结构如下:

  • 链表头 head 到环入口 entry 的距离为 L(节点数);
  • 环的长度为 C(环中节点数);
  • 快慢指针相遇时,慢指针 slow 已走 S 步,快指针 fast 已走 2S 步(因快指针每次走2步,慢指针每次走1步,速度比2:1)。

相遇时的关键结论:

  • 快指针比慢指针多走的步数 2S - S = S,必然是环长度 C 的整数倍(快指针在环内绕圈追赶慢指针,多走的路程就是绕环的圈数),即 S = k*Ck 为正整数);
  • 慢指针走的路程 S = L + MM 是相遇点 meet 到环入口 entry 的距离,0 ≤ M < C);
  • 结合以上两式:L + M = k*CL = k*C - ML = (k-1)*C + (C - M)

该式的物理意义:从链表头 head 到入口 entry 的距离 L,等于从相遇点 meet 绕环 (k-1) 圈后,再走到入口 entry 的距离C - M 是相遇点到入口的剩余距离,绕 k-1 圈后回到相遇点,再走 C-M 步到入口)。

2. 算法执行步骤

阶段1:环检测(快慢指针相遇)
  • 初始化:快指针 fast 和慢指针 slow 均指向链表头 head
  • 循环条件:fast != NULL && fast->next != NULL(避免快指针访问空指针崩溃);
  • 指针移动:slow 每次走1步,fast 每次走2步;
  • 相遇判断:若 fast == slow,说明链表有环,记录相遇点 meetmeet = slow = fast),进入阶段2;若循环结束未相遇,说明无环,返回 NULL
阶段2:入口定位(双指针同步移动)
  • 初始化:新指针 pcur 指向链表头 headmeet 保持在之前的相遇点;
  • 循环条件:pcur != meet
  • 指针移动:pcurmeet 每次均走1步(速度比1:1);
  • 终止条件:当 pcur == meet 时,两指针同时到达环的入口节点,返回该节点(pcurmeet 均可)。

3. 算法特性

  • 时间复杂度:O(n),两阶段遍历的总节点数不超过链表总长度(无环时遍历到尾,有环时相遇+入口定位的总步数为 O(n));
  • 空间复杂度:O(1),仅使用4个指针(fastslowmeetpcur),无额外空间开销;

二、流程图

开始
初始化: fast=head, slow=head
继续循环检测环
返回 NULL(无环)
结束
slow = slow->next; fast = fast->next->next
fast == slow?
初始化: meet=slow, pcur=head
pcur == meet?
返回 meet(环入口)
pcur=pcur->next; meet=meet->next

三、关键逻辑总结

  1. 「快慢指针相遇」是链表有环的充要条件,且相遇点必然在环内;
  2. 「头指针+相遇点指针同步移动」的本质是利用数学推导的距离等价性,无需计算环长和头到入口的距离,直接定位入口;
  3. 代码复用了环检测的逻辑,在相遇后无缝衔接入口定位,结构简洁且效率最优。

四、代码:

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

  • 本节完…
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值