当前位置: 首页 > article >正文

双指针巧解链表有环问题

链表有环问题可以使用双指针技巧轻松解决。

  • 判断链表是否有环问题,可以通过设置快慢指针同向遍历链表,若相遇则有环。

  • 找环入口问题,也可以通过设置快慢指针同向遍历链表,寻找相遇点。不同的是,当两指针相遇后,快指针回到链表头节点,慢指针留在相遇节点,两者同速遍历,二次相遇点一定是环入口

下面两道题精选于牛客网面试必刷TOP101,相信我,十分简单,非常容易理解!

欢迎来到我的博客 柿子先生的半亩良田,查看更多优质内容。


BM6 判断链表中是否有环

描述

判断给定的链表中是否有环。如果有环则返回 true,否则返回 false。

思路

双指针

线性与环形链表特点:
  • 线性链表末尾一定有 null。

  • 环形链表的环一定在末尾,末尾没有 null 了。

线性与环形链表结束条件:
  • 线性链表的结束条件就是遍历到 null。

  • **环形链表会不断循环,需要借助双指针才能结束。**同向访问的双指针,因为速度不同,只要有环,二者一定能相遇。

F1:双指针

步骤
  1. 设置快慢两指针,初始都指向链表头;
  2. 遍历链表,快指针每次走两步,慢指针每次走一步;
  3. 如果快指针到了链表末尾,说明没有环,因为它每次走两步,所以要验证连续两步是否为 null;
  4. 如果链表有环,那快慢双指针会在环内循环,因为快指针每次走两步,因此快指针会在环内追到慢指针,二者相遇就代表有环。

双指针找环入口问题

代码
public boolean hasCycle(ListNode head) {
    // 先判断链表为空的情况
    if (head == null)
        return false;
    // 1. 设置快慢两指针,初始都指向链表头
    ListNode fast = head;
    ListNode slow = head;
    // 2. 遍历链表
    // 3. 如果没环,fast 会先到链表尾
    while (fast != null && fast.next != null) {
        // 快指针移动两步
        fast = fast.next.next;
        // 慢指针移动一步
        slow = slow.next;
        // 相遇则有环
        if (fast == slow)
            return true;
    }
    // 到末尾,则没有环
    return false;
}
时间复杂度

O ( M ) O(M) O(M)

最坏情况下遍历链表 n 个节点。

空间复杂度

O ( 1 ) O(1) O(1)

仅使用了两个指针,没有额外辅助空间。


BM7 链表中环的入口点

描述

给一个长度为 n 链表,若其中包含环,请找出该链表的环的入口结点,否则,返回 null。

分解任务
  1. 判断链表是否有环;
  2. 在有环的链表中找到环的入口。

思路

双指针

如何找到环的入口

一个有环链表。

假设快指针在环中走了 n n n 圈,慢指针走了 m m m 圈,两者相遇。

链表头节点到环入口点距离为 x x x,环入口到相遇点距离为 y y y,相遇点到环入口点距离为 z z z

快指针一共走了 x + n ( y + z ) + y x + n(y + z) + y x+n(y+z)+y 步,慢指针一共走了 x + m ( y + z ) + y x + m(y + z) + y x+m(y+z)+y 步。

快指针走的步数是慢指针的两倍,则
x + n ( y + z ) + y = 2 ( x + m ( y + z ) + y ) x + n(y + z) + y = 2(x + m(y + z) + y) x+n(y+z)+y=2(x+m(y+z)+y)
进一步推导,
x + y = ( n − 2 m ) ( y + z ) x+y=(n-2m)(y+z) x+y=(n2m)(y+z)
因为环的大小是 y + z y+z y+z,说明从链表头经过环入口到达相遇地方经过的距离等于整数倍环的大小。那么,我们从头开始遍历到相遇位置,和从相遇位置开始在环中遍历,会使用相同的步数,而双方最后都会经过入口到相遇位置这 y y y 个节点,说明这 y y y 个节点它们就是重叠遍历的,那它们从入口位置就相遇了,这样就找到了入口节点。

F1:双指针

步骤
  1. 使用 BM6 方法判断链表是否有环,并找到相遇节点。
  2. 慢指针继续在相遇节点,快指针回到链表头,两个指针同步逐个元素开始遍历链表。
  3. 两者再次相遇的地方就是环的入口。

双指针找环入口问题

代码
// 寻找链表中环的入口节点
public ListNode entryNodeOfLoop(ListNode pHead) {
    ListNode slow = hasCycle(pHead);
    // 没有环
    if (slow == null)
        return null;
    // 有环
    // 快指针回到表头
    ListNode fast = pHead;
    // 再次相遇即是环入口
    while (fast != slow) {
        fast = fast.next;
        slow = slow.next;
    }
    return slow;
}

// 判断有没有环
public ListNode hasCycle(ListNode head) {
    // 先判断链表为空的情况
    if (head == null)
        return null;
    // 快慢双指针
    ListNode fast = head;
    ListNode slow = head;
    // 如果没环,快指针会先到链表尾
    while (fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
        // 相遇则有环,返回相遇的节点
        if (slow == fast) {
            return slow;
        }
    }
    // 到末尾说明没有换,返回 null
    return null;
}
时间复杂度

O ( n ) O(n) O(n)

最坏情况下遍历链表两次

空间复杂度

O ( 1 ) O(1) O(1)

使用了常数个指针,没有额外辅助空间


http://www.kler.cn/a/7339.html

相关文章:

  • Cursor安装Windows / Ubuntu
  • 【nginx】client timed out和send_timeout的大小设置
  • mysql数据迁移PolarDB
  • 高级数据结构——hash表与布隆过滤器
  • STM32寄存器结构体详解
  • Spring:bean的配置
  • 算法设计-hw2
  • 晶振02——晶振不能放置在PCB边缘
  • windows搭建ftp及原理(小白向)
  • 国内怎么玩chatGPT中文版-国内怎么玩chatGPT4
  • C# | 上位机开发新手指南(七)加密算法
  • 【Hello Linux】线程控制
  • springboot 统一日志
  • 百度云【人脸识别】
  • 信息论小课堂:通信趋势(万物互联)
  • Rhinoceros 7 (三维建模软件犀牛7)图文安装教程
  • 使用Docker创建一个WebSphere服务
  • (Week 15)综合复习(C++,字符串,数学)
  • EasyCVR通道收藏的功能拓展
  • LeetCode算法小抄--滑动窗口算法
  • openEuler Linux 部署 HadoopHA
  • 安装k8s工具之二-kubeasz
  • session和jwt哪个更好
  • Visio导入CAD绘图问题总结-更改形状线条颜色问题解决
  • Java ---多态
  • 利用CMake工具从源码编译出osgEarth库