Leetcode 31

1 题目

142. 环形链表 II

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

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

不允许修改链表。

示例 1:

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

示例 2:

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

示例 3:

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

提示:

  • 链表中节点的数目范围在范围 [0, 104]
  • -105 <= Node.val <= 105
  • pos 的值为 -1 或者链表中的一个有效索引

2 思路

实际上这是141. 环形链表 - 力扣(LeetCode)的进阶版

1 首先判断是否有环(可以按照easy的策略,快慢指针相遇)

2 返回环节点,按照图中的路径,其实是一点数学呢,相遇以后,其中一个指针到头节点,快慢指针以相同速度前进,再次项羽点就是环节点

3 注意非空,注意临界条件

3 代码实现(c)

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) {
    ListNode *one = head ;
    ListNode *two = head;
    while(one != NULL && one-> next != NULL){
        one = one -> next -> next ;
        two = two -> next ;
        if(one == two){
            two = head ;
            while(one != two){
                one = one -> next;
                two = two -> next;
                } 
             return one ;   
            }
        } 
           return NULL;
    }

    

初版,失败的代码()

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) {
    ListNode *one = head->next;
    ListNode *two = head;
    while(one != NULL){
        one = one -> next ;//一开始是想让one,two做两个小跟班,实际上逻辑行不通
        two = two -> next ;
        if(one == two){
            two = head ;
            while(one != two){
                one = one -> next;
                two = two -> next;
                if(one == two){
                    break;
                } 
            }
            return one;
        }
    }
    return NULL;
    
}

代码中的问题

  1. 初始指针位置错误 代码中初始化为 one = head->next; two = head;,若链表为空(head == NULL),会直接访问 head->next 导致空指针异常。

  2. 快慢指针步长错误 检测环时,快指针应每次走 2 步,慢指针每次走 1 步(才能利用 "相对速度" 相遇)。但代码中 one(快指针)和 two(慢指针)每次都只走 1 步(one = one->next; two = two->next;),相当于两个指针同速前进,若有环也可能永远不相遇。

正确的环起始节点检测逻辑

要找到环的起始节点,核心利用快慢指针相遇的性质

  1. 检测环是否存在 :快指针(每次 2 步)和慢指针(每次 1 步)从表头出发,若相遇则有环;若快指针走到终点(NULL)则无环。

  2. 找到环的起始节点:当快慢指针相遇时,将其中一个指针重置到表头,两者以相同速度(每次 1 步)前进,再次相遇的节点就是环的起始节点。

    原理 :设表头到环起点距离为 a,环起点到相遇点距离为 b,环长度为 c。相遇时快指针走了 a + b + k*ck 为圈数),慢指针走了 a + b。由于快指针速度是慢指针的 2 倍,因此 a + b + k*c = 2*(a + b),化简得 a = k*c - b,即表头到环起点的距离 = 相遇点到环起点的距离(绕环 k-1 圈后)。因此两指针同速前进时,会在环起点相遇。

修正后的代码(示例)

cpp 复制代码
struct ListNode *detectCycle(struct ListNode *head) {
    if (head == NULL || head->next == NULL) {
        return NULL; // 空链表或只有一个节点,无环
    }

    ListNode *slow = head;
    ListNode *fast = head;

    // 第一步:检测环是否存在(快慢指针相遇)
    while (fast != NULL && fast->next != NULL) {
        slow = slow->next;       // 慢指针走1步
        fast = fast->next->next; // 快指针走2步
        if (slow == fast) {      // 相遇,有环
            // 第二步:找环的起始节点
            slow = head;         // 慢指针重置到表头
            while (slow != fast) {
                slow = slow->next;
                fast = fast->next; // 同速前进,再次相遇即为环起点
            }
            return slow;
        }
    }

    return NULL; // 无环
}

总结

  • 原代码因指针初始位置和步长错误,无法正确检测环和起始节点。
  • 正确逻辑需利用快慢指针的 "2 倍速差" 相遇检测环,再通过重置指针同速前进找到环起点。
相关推荐
早睡冠军候选人1 小时前
Ansible学习----管理复杂的 Play 和 Playbook 内容
运维·学习·云原生·ansible
LBuffer2 小时前
破解入门学习笔记题四十六
数据库·笔记·学习
Blossom.1184 小时前
移动端部署噩梦终结者:动态稀疏视觉Transformer的量化实战
java·人工智能·python·深度学习·算法·机器学习·transformer
Yurko134 小时前
【计网】基于三层交换机的多 VLAN 局域网组建
网络·学习·计算机网络·智能路由器
轻微的风格艾丝凡4 小时前
卷积的直观理解
人工智能·深度学习·神经网络·算法·计算机视觉·matlab·cnn
月下倩影时4 小时前
视觉进阶篇——机器学习训练过程(手写数字识别,量大管饱需要耐心)
人工智能·学习·机器学习
田梓燊6 小时前
红黑树分析 1
算法
MasterLi80237 小时前
我的读书清单
android·linux·学习
hssfscv7 小时前
JAVA学习笔记——集合的概念和习题
笔记·学习
晚风吹长发7 小时前
二分查找算法+题目详解
c++·算法·二分查找