环形链表之快慢指针

前言

对于环形链表,通过快慢指针,如果存在环,这这两个指针一定会相遇,这是一种经典的判断环或是应用于环问题的思想。

一、案例

1、环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

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

如果链表中存在环 ,则返回 true 。 否则,返回 false 。
在这里插入图片描述

注:以O(1)内存进阶。

2、环形链表II

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

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

注:不允许修改 链表。

注:以O(1)内存进阶

二、题解

1、环形链表

1)可以直接遍历,把遍历的结点用Set记录下来,然后记录前查看set中是否有该节点。
2)对于O(1)内存进阶,
A)可以通过修改遍历过的节点的value为一个特殊值,然后一直遍历,如果中途碰到有value为特殊值,说明遍历过,即有环。
B)可每次遍历过之后,就修改前驱节点的next指向为相反的指向,不管是否有环,都不会在遍历中出现死循环,而是从左出即有环,从右出则无环。
C)快慢指针,可以通过快慢指针,就像在操场跑圈一样,速度快的能和速度慢能相遇。

2、环形链表II

1)从第一个的基础上,1)和2)的A)两种方式上都容易修改得来;而B)不适用;
2)对于C),可以得到最后相遇的地方,这个节点一定在环内,设为end节点。外层从头节点遍历到end节点,内层循环从end节点开始遍历,直到下一个end节点,看中途是否会碰到外层循环的中途节点。

3、源码

package com.xhu.offer.tencent;

import java.util.HashSet;
import java.util.Set;

//环形链表
public class HasCycle {
   
    public boolean hasCycle(ListNode head) {
   
        //一直next直到next == null 或者next到已经访问过的节点,分别返回true 和 false;
        Set<ListNode> mark = new HashSet<>();
        while (head != null) {
   
            if (mark.contains(head)) return true;
            mark.add(head);
            head = head.next;
        }
        return false;
    }

    //需要消耗O(1)内存来进阶
    public boolean hasCycle2(ListNode head) {
   
        //每向前走一步就改变链接方向,如果循环结束的最后一个节点是初始节点则有环。
        if (head == null || head.next == null) return false;
        ListNode point = head, pre1 = null, pre2;
        while (point.next != null
### 快慢指针算法链表中的应用场景及实现 快慢指针是一种常见的双指针技巧,在处理链表问题时非常有效。它通过两个速度不同的指针遍历链表来解决问题,能够显著降低时间复杂度并优化空间需求。 #### 应用场景分析 1. **检测环形链表** 使用快慢指针可以判断单向链表是否存在环结构。如果存在环,则快指针最终会追上慢指针;如果没有环,则快指针会先到达链表末尾[^1]。 2. **寻找环的入口节点** 当确认链表有环后,可以通过调整其中一个指针重新指向头结点的方式找到环的具体入口位置[^2]。 3. **查找中间节点** 利用快慢指针可以在一次遍历中定位到链表的中间节点。当快指针移动两步而慢指针只前进一步时,慢指针所处的位置即为链表中心。 4. **获取倒数第 K 个节点** 设置快指针先行走 K 步之后再启动慢指针同步前进,这样当快指针抵达终点时,慢指针正好处于目标位置之前的一个节点上。 #### 实现代码示例 以下是几个典型例子及其对应的 C++ 或 Python 的解决方案: ##### 检测是否有环 ```cpp ListNode* hasCycle(ListNode* head) { if (!head || !head->next) return nullptr; ListNode *slow = head, *fast = head; while (fast && fast->next){ slow = slow->next; // 移动一步 fast = fast->next->next; // 移动两步 if(slow == fast){ // 如果相遇则说明有环 return true; } } return false; // 遍历结束无环返回false } ``` ##### 找到环的起始点 ```python def detectCycle(head): if not head or not head.next: return None slow, fast = head, head # 查看是否成环 while fast and fast.next: slow = slow.next # 慢指针每次向前一格 fast = fast.next.next # 快指针每次跳过两格 if slow == fast: # 发现有交集 break # 跳出循环准备找入环口 else: # 若未发现任何交叉情况退出while loop return None # 表明不存在闭环直接return none即可 # 将其中任意一个重置回起点再次同时出发直到两者再度交汇于入环处为止 slow = head # 让慢指针回到头部开始新一轮迭代过程 # 这次保持相同步伐大小逐步靠近直至碰面那一刻就是我们要找的那个特殊地址啦! while slow != fast: slow = slow.next # 均匀增量推进至相逢之地~ fast = fast.next return slow # 返回该共同坐标作为结果输出。 ``` ##### 获取链表中间节点 ```java public class MiddleNodeFinder { public static ListNode findMiddle(ListNode head) { if (head == null || head.next == null) { return head; } ListNode slow = head; ListNode fast = head; while(fast != null && fast.next != null){ slow = slow.next; // Move one step at a time. fast = fast.next.next; // Move two steps ahead. /* When 'fast' reaches the end of list, 'slow' will be pointing to middle node */ } return slow; } } ``` ##### 寻找倒数第K个节点 ```javascript function getKthFromEnd(head, k) { let fast = head, slow = head; for(let i=0;i<k;i++) { // Advance `fast` pointer by 'k' positions first. if(!fast) return null; fast = fast.next; } while(fast !== null){ // Then move both pointers until 'fast' hits the tail. fast = fast.next; slow = slow.next; } return slow; // Now 'slow' points exactly where we need it - k-th from last element ! } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值