13.环形链表Ⅰ(双指针)14.环形链表Ⅱ(双指针)(有公式推导哦)

本文探讨了如何使用双指针方法判断链表是否存在环,并在存在环时找到环的入口点。通过分析快慢指针的移动策略,揭示了如何避免死循环并确定环的起点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

环形链表 试试这题

给定一个链表,判断链表中是否有环。

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

如果链表中存在环,则返回 true 。 否则,返回 false 。

示例 1:

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

示例 3:
在这里插入图片描述

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

解题思路:带环的链表让人头疼的地方就是不能遍历链表,因为如果你遍历链表就会链表死循环,这是我们就只有有双指针来解决这个问题,定义两个指针fast和slow指针,fast指针每次走两步,slow指针每次走一步,当两个指针同时从head头指针开始遍历,因为fast指针移动比较快先进入环里面(如果有的话)
,然后因为fast指针每次都比slow指针快一步,所以在里面形成了追击问题,每次再环里面两个指针的差距都会减少一,直到最后相遇,如果中间fast指针或fast->next为NULL,则直接判为不带环

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
typedef struct ListNode Node;
bool hasCycle(struct ListNode *head) {
    //如果只有一个结点或者头结点为空则一定不是带环链表
    if(head == NULL || head->next == NULL)
        return false;
    Node* fast = head;
    Node* slow = head;
    //判断fast指针或fast->next指针是否为空
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(slow == fast)//如果slow和fast指针相遇,那么就是带环指针
            return true;
    }
    return false;//如果跳出循环,说明链表不带环
}

那为什么sloe指针走一步fast指针走两步呢?如果fast指针走3,4,5…N步行不行呢?

下证:
因为fast指针和slow指针都会进环的(如果有环的话)那就假设指针都已经进环了,
在这里插入图片描述
这时fast指针其实就是在在这个环里在追赶slow指针,只要fast追赶slow超了一圈,那就追上了,,假设fast和slow相差N步,(fast每次2步,slow每次1步)每走一次他们的距离就减少1,距离变化:N-1,N-2,N-3…1,0。最后变成0,但是要是fast每次走3步呢?每次距离较少2,N-2,N-4…如果环中的节点数为奇数个最后指针一步的时候就变成-1距离了,那就又相差一步了,这样下去又变成死循环了,如果是4步也是同样的道理,每次快3步,结点数为偶数,那也会死循环,所以总结就是相差距离N必须是每次减少距离的倍数(相加距离N % (fast每次步数 - slow每次步数) == 0)才不会死循环,又因为1是任何数的因数所以每次fast指针不slow指针快1步。

环形链表 II(在带环链表的入口点)试试这题

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

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。

说明:不允许修改给定的链表。

示例 1:

在这里插入图片描述

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

示例 3:

在这里插入图片描述

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

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
 
typedef struct ListNode Node;
struct ListNode *detectCycle(struct ListNode *head) {
    //先找到相遇点
    Node* fast = head;
    Node* slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
            break;
    }
    //判断链表是否带环,如果fast或fast->next为NULL,说明不带环
    if(fast == NULL || fast->next == NULL)
        return NULL;
    //如果带环则,让fast指针或slow指针和head指针一起走,他们相遇的点就是换的入口点
    slow = head;
    while(fast != slow)
    {
        fast = fast->next;
        slow = slow->next;
    }
    return fast;
}

在这里插入图片描述
head到入环口距离k,入环口到相遇位置距离x,环的周长为C.
slow指针走距离,fast指针走k+x+NC距离,N是一个系数,环越大fast在和slow指针先相遇前绕环的圈数越少,环越小绕环次数越大(N越大),fast = 2*slow,
2(x+k)= k+x+NC
x+k = NC
k = NC-x
k = (N-1)C + C - x
所以头指针到入环位置等于相遇位置到入环位置,因为中间fast指针已经饶了N-1圈环长所以所以还是是相遇位置,最特殊的时候就是当N=1时,两个位置相等,这样通过fast指针和head指针同时走相遇时就可以找到入口点

<think>好的,我现在需要帮助用户寻找LeetCode上链表相关的练习题和解法。首先,我得回忆一下用户提供的引用内容,看看里面提到的题目有哪些。引用[1]提到合并链表,引用[2]列出了多个题目,比如203移除链表元素、707设计链表、206反转链表142环形链表、19删除倒数第N个节点、面试题02.07链表相交。引用[3]补充了更多题目,比如链表的中间节点、链表分割、回文结构、相交链表环形链表II、复制带随机指针链表。引用[4]主要讲反转链表的解法。 接下来,我需要将这些题目分类整理,方便用户逐步练习。可能的基础题包括移除链表元素、反转链表、设计链表,这些题目帮助掌握链表的基本操作。然后是进阶题,比如环形链表、合并有序链表、删除倒数节点,这些涉及快慢指针和递归等技巧。最后是综合应用题,如链表相交、回文结构、复杂链表的复制,这些题目需要综合运用多种方法。 在整理每个题目时,需要简要说明解题思路和关键点,比如移除元素可能需要虚拟头节点,反转链表可以用迭代或递归,环形链表使用快慢指针找环。同时,要确保提到的解法与引用中的内容相符,比如引用[4]提到的反转链表的自解代码,可以建议用户参考。 另外,用户可能需要相关的学习资源,所以应该根据引用提供的链接,比如代码随想录的刷题指南,推荐给用户。同时,提醒用户注意链表处理的常见技巧,如虚拟头节点、指针操作等,这些都是引用[2]和[3]中提到的内容。 最后,生成相关问题时要考虑用户可能的后续需求,比如不同难度题目的区分、时间复杂度的优化、具体题目的代码实现等。这样用户可以根据自己的进度进一步探索。</think>### LeetCode链表练习题及解法指南 链表是数据结构中的基础题型,以下整理LeetCode经典题目及解法思路,按难度分级并标注关键技巧: --- #### **一、基础操作题(掌握指针操作)** 1. **203. 移除链表元素** - **要求**:删除链表中所有等于给定值的节点。 - **解法**:使用**虚拟头节点**简化删除逻辑,避免处理头节点特殊情况[^2]。 - **示例代码**: ```python def removeElements(head, val): dummy = ListNode(0, head) curr = dummy while curr.next: if curr.next.val == val: curr.next = curr.next.next else: curr = curr.next return dummy.next ``` 2. **206. 反转链表** - **要求**:将链表完全反转。 - **解法**:迭代法(双指针)或递归法。迭代法中维护`prev`和`curr`指针逐步反转[^4]。 - **关键代码**: ```python def reverseList(head): prev, curr = None, head while curr: next_node = curr.next curr.next = prev prev = curr curr = next_node return prev ``` 3. **707. 设计链表** - **要求**:实现链表的增删查操作。 - **关键点**:使用虚拟头节点统一操作逻辑,注意边界条件。 --- #### **二、进阶技巧题(快慢指针/递归)** 4. **142. 环形链表 II** - **要求**:判断链表是否有环,并返回环的入口。 - **解法**:快慢指针相遇后,重置指针找入口[^3]。 - **公式推导**:设环前长度$L$,环周长$C$,相遇时快指针比慢多走$C$,可证入口在$L$处[^3]。 5. **21. 合并两个有序链表** - **要求**:合并两个升序链表为一个新链表。 - **解法**:递归(比较头节点大小,递归合并剩余部分)或迭代(双指针遍历)。 6. **19. 删除链表的倒数第N个节点** - **要求**:删除倒数第N个节点。 - **解法**:快指针先走$N$步,再同步移动快慢指针[^2]。 --- #### **三、综合应用题(多方法结合)** 7. **160. 相交链表** - **要求**:找到两个链表的相交节点。 - **解法**:双指针遍历两链表,路径长度互补消除差值。 8. **234. 回文链表** - **要求**:判断链表是否为回文。 - **解法**:快慢指针找中点,反转后半部分后比较。 9. **138. 复制带随机指针链表** - **要求**:深拷贝含随机指针链表。 - **解法**:哈希表映射原节点到新节点,分两步处理`next`和`random`指针。 --- #### **四、学习资源推荐** - **系统教程**:参考[代码随想录的链表专题](https://leetcode-cn.com/circle/article/wGp7Y9/)。 - **高频题目**:按顺序练习上述题目,逐步掌握**虚拟头节点、双指针、递归**等技巧。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

hyzhang_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值