环形链表II

这篇博客介绍了如何利用快慢指针法解决链表中环形结构的检测及找到环的入口节点的问题。通过设定一个指针每次前进一步,另一个指针每次前进两步,当两个指针相遇时确定链表存在环。接着,通过一个指针从头节点开始,另一个指针从相遇点开始,它们会在环的入口节点相遇。提供了C语言的代码实现作为示例。

142.环形链标II

题目地址:https://leetcode-cn.com/problems/linked-list-cycle-ii/

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改链表。
示例 1:
环形链表1

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:
环形链表2

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 3:
环形链表3

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

思路

总体思路是快慢指针法,就是一个show指针一次走一步fast指针一次走两步,如果有环的话,当两个指针同时进入环后,他们之间的举例以一的次数减少,每次移动间隔减少一,他们必定会相遇,如果是三和五没有尝试读者可以尝试在评论区说一下可不可以。

下面就是他们如果相遇fast就是快指针走的路程 slow就是慢指针走的路程 环外的路程为a 环的长度为b当他们相遇的时候:
fast=2slowfast=2slowfast=2slow
fast=slow+nbfast=slow+nbfast=slow+nb(相遇的时候fast比slow多走环的整数倍)
所以:
slow=nbslow=nbslow=nb
fast=2nbfast=2nbfast=2nb
又因为:
链表长度的入口节点位置:s=a+nbs=a+nbs=a+nb
slow+a=sslow+a=sslow+a=s
所以ptr=head向前每次向前一其必和slow在入口节点相遇
相遇的地方就是a 再来一个双指针法,此指针和slow 一起向前走 a 步后,两者在入口节点重合。

代码实现(c语言版本)

struct ListNode *detectCycle(struct ListNode *head) {
    if(!head)
    {
        return NULL;
    }
    struct ListNode *show=head;
    struct ListNode *fast=head;
    struct ListNode *ptr=head;
    while(fast->next&&fast->next->next)
    {
        show=show->next;
        fast=fast->next->next;
        if(show==fast)
        {
            while(show!=ptr)
            {
                show=show->next;
                ptr=ptr->next;
            }
            return show;
        }
    }
    return NULL;
}
### 环形链表 II 的核心概念 环形链表 II 是一种经典的算法问题,主要涉及检测单向链表是否存在环以及找到环的入口节点。该问题的核心在于通过双指针技术(快慢指针)来解决复杂度较低的时间和空间需求。 #### 双指针法原理 在环形链表 II 中,可以利用两个指针——一个慢指针 `slow` 和一个快指针 `fast` 来遍历链表。具体来说,慢指针每次移动一步,而快指针每次移动两步。如果链表存在环,则这两个指针最终会在某个节点上相遇[^1]。 当发现两者相遇时,可以通过以下推导得出如何定位环的起始位置: 设: - 链表头结点到环起点的距离为 \(a\), - 环起点到首次相遇点的距离为 \(b\), - 环剩余部分长度为 \(c\), 则有如下关系成立: \[ \text{slow} \times (a+b) = \text{fast} \times (a+b+c) \] 由于 fast 移动速度是 slow 的两倍,因此可得方程: \[ 2(a + b) = a + n(b + c) + b \] 化简得到: \[ a = nc - nb = (n-1)(b+c)+c \] 这意味着从头节点出发的一个新指针与当前位于相遇点的指针同步前进,它们将在环的入口处再次重合[^4]。 #### C++ 实现代码示例 以下是基于上述理论的一种高效解决方案实现方式: ```cpp class Solution { public: ListNode *detectCycle(ListNode *head) { if (!head || !head->next) return nullptr; ListNode* slow = head; ListNode* fast = head; bool hasCycle = false; while(fast && fast->next){ slow = slow->next; // Move one step at a time. fast = fast->next->next; // Move two steps at a time. if(slow == fast){ // If they meet, there's a cycle. hasCycle = true; break; } } if(!hasCycle) return nullptr; // No cycle detected. slow = head; // Reset 'slow' to start from the beginning. while(slow != fast){ slow = slow->next; fast = fast->next; } return slow; // They will meet again at the entrance node. } }; ``` 此代码片段展示了如何使用双指针技巧有效地解决问题并返回环的第一个节点地址或者 NULL 表明无循环情况发生[^3]。 #### 时间与空间复杂度分析 时间复杂度方面,整个过程仅需一次完整的列表扫描即可完成操作,故总体时间为 O(n),其中 n 表示链表中的节点总数;至于额外使用的内存资源仅为常数级别变量存储,所以整体的空间消耗也是恒定不变即 O(1)[^2]。 ### 性能优化与其他方法探讨 除了采用双指针外还可以考虑其他策略比如哈希集合记录访问过的每一个节点直到重复为止从而判断是否有回路形成但是这种方法会增加更多的辅助储存单元使得实际运行效率降低并且违背了原题对于低开销的要求。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学c的小李

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值