详细总结快慢指针的在链表中的常见题型

news2024/11/16 9:22:55

常见快慢指针题型

  • 1.找出链表中间结点
  • 2.找到倒数第K个结点
  • 3.判断环形链表
  • 4.找到环形链表的入口(进阶)
  • 5.相交链表

1.找出链表中间结点

在这里插入图片描述

双指针进阶解法

1.定义两个指针,一个快指针,一个慢指针。
2.快指着一次走两步,慢指针一次走一步
3.考虑快指针指向哪里时,我们的慢指针刚好走到中间结点

用两个指针 slowfast 一起遍历链表。slow 一次走一步,fast 一次走两步。那么当 fast 到达链表的末尾时,slow 必然位于中间。

1.定义快慢指针
2.快指针一次走一步,慢指针一次走两步
3.当快指针为null或快指针的next为null时退出循环

在这里插入图片描述

前提讲到了,如果有两个中间结点,则返回第二个中间结点,所以我们再看一下这个解法对于结点个数为偶数个的还是否合适

在这里插入图片描述
看图可以知道,无论该链表是奇数个还是偶数个,都不会影响最终的结果。

判断条件:
fast==null时,或者fast.next==null时,退出循环。
注意:由于逻辑运算符&&是先判断左侧,左侧为真再去判断右侧,如果我的fast已经是null,那么如果我将fast.next写在左侧就会产生空指针异常,所以我们需要先判断fast是否为null,也就是将fast!=null写在左侧

代码如下

class Solution {
    public ListNode middleNode(ListNode head) {
    //定义两个指针,一个快指针,一个慢指针
        ListNode slow = head, fast = head;
        while (fast != null && fast.next != null) {
            slow = slow.next;//慢指针一次走一步
            fast = fast.next.next;//快指针一次走两步
        }
        return slow;//返回慢指针
    }
}

复杂度分析:

  • 时间复杂度O(n),其中 N 是给定链表中的结点数目。
  • 空间复杂度O(1),只需要常数空间存放 slow 和 fast 两个指针。

2.找到倒数第K个结点

在这里插入图片描述

解题思路:

定义一个快慢指针,先让快指针走K步,然后慢指针和快指针一起走,当快指针为null时,我们的慢指针就是倒数第K个数。

究竟上面的原理是如何推理出来的呢?

我们看图演示

在这里插入图片描述

第一步:先让快指针cur走K步,如图二
第二步:快指针,慢指针一起走,当快指针为null时退出循环
第三步:此时prev就是倒数第K个结点,返回prev就行

具体过程如下

在这里插入图片描述

使用如图的快慢指针,首先让快指针先行k步,然后让快慢指针每次同行一步,直到快指针指向空节点,慢指针就是倒数第K个节点

代码入下
注意注释!!!

public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
		 //判断链表是不是为空,并且k不能小于等于0	
        if (k<=0||head==null) {
            return null;
        }
        int n=0;
        ListNode prev=head;
        ListNode cur=head;
        while (n<k) {
        //判断cur是否为null
        //如果为null返回null
            if (cur==null) {
               return null;
            }
            cur=cur.next;
            n++;
            //如果将if放到下面,
            //那么判断倒数第n个结点时就会出错,n是链表长度,
            //也就是查找链表头节点时会报错
            //if (cur==null) {
            // return null;
            // }
        }
        while (cur!=null) {
            prev=prev.next;
            cur=cur.next;
        }
        return prev;
    }
 }

作者总结:

有一个极端条件就是当倒数第K个结点是头节点时,不能将if放到我在代码注释的那个位置,如下图
在这里插入图片描述

如果在下面判断当cur == null时就退出,那么存在一种情况就是当我的cur==null时,我的prev就是倒数第5个结点符合条件,如果此时退出结果就会和预期结果不一样,所以我们将判断的条件写在了上面,那样的话,只有当我们的n没有符合条件时并且cur==null,才返回null,这样就避免了上面的错误

输入一个链表,输出该链表中倒数第k个结点oj题

3.判断环形链表

在这里插入图片描述

题目描述:

给你一个链表,判断是否带环,如果带环返回true否则返回false

快慢指针解法

假设同学「A」和同学「B」相约一起跑步,他们一起从宿舍楼出来,由于「A」跑得快,所以「A」率先到达操场进行跑圈,如果不是跑圈(没有环),那么「B」永远都不会追上「A」,等到「B」同学进入操场时,由于「A」的速度快,它一定会在某个时刻与「B」相遇,即比「B」多跑了若干圈.

我们可以借助上面的思路来解决本题:

1.设置一个快慢指针
2.快指针一次走二步,慢指针一次走一步
3.当快指针为空或者快指针的下一个结点为空退出循环(这样就说明不带环)返回false
4.当慢指针追上快指针时,说明带环,返回true

我们看下图:

在这里插入图片描述
代码如下

public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode A=head;//快指针
        ListNode B=head;//慢指针
        while(A!=null&&A.next!=null) {
            A=A.next.next;//快指针一次走两步
            B=B.next;//慢指针一次走一步
            //当两个结点相同时,意思就是相遇了
            if(A==B) {
                return true;//返回正确
            }
        }
        return false;//当出了循环说明这个链表没有环,返回错误
    }
}

复杂度分析
时间复杂度:O(N)O(N)O(N),其中 NNN 是链表中的节点数。

空间复杂度:O(1)O(1)O(1)。我们只使用了两个指针的额外空间
复杂度分析:

  • 时间复杂度O(n),其中 N 是给定链表中的结点数目。

当链表中不存在环时,快指针将先于慢指针到达链表尾部,链表中每个节点至多被访问两次。
当链表中存在环时,每一轮移动后,快慢指针的距离将减小一。而初始距离为环的长度,又因为慢指针最多走N次,因此至多移动 N 轮。(下面讲解)

  • 空间复杂度O(1),只使用了两个指针额外空间

为什么慢指针至多移动N次?(N是链表长度)

我们可以想象,两个人在跑步,由于「A」同学跑的快,所以在一定会遇到「B」同学,前提不是讲到了嘛,「A」同学的速度是「B」同学的二倍,所以当「B」同学跑完一圈时,「A」同学一定跑完了两圈(A的起点为B同学进入操场时,A同学的当前位置来算),所以无论怎样,他们都一定会相遇,

为什么快指针一定是一次两步,如果一次三步,一次四步可不可以

举例

答案是不可以的!可能在数学中你们是能算出相遇的时间或者相遇时移动的次数(那是因为数学中有小数的存在),但是链表不一样,它的每一步都是整数,不存在小数这一说,就像小时候玩的飞行棋,假如在距离你一个格子的位置有一个宝箱,那么你走四步,三步,甚至是两步你都不会吃到这个宝箱,只能眼睁睁的看着和它擦肩而过,就和你处心积虑想要跟你的女神制造一个偶遇的机会一样,她三点出门,你呢?三点零一分走到她的宿舍楼底下,那么你们就不会相遇,这就是错过,一分一毫都不能差。所以我们在追赶爱情的过程中,把握住时宜,宁可早来,不可迟到,可能你不知道她什么时候回来,但是你要做好准备,哪怕早来六个小时,哪怕是雪天,哪怕是你站在街上为了耍酷只穿一个薄薄的外套而被冻成冰棍,你也不要放弃,这些付出,在你最后和她在一起时,都是值得的!(故事来B站某位宋老师,咳咳有点跑题)

回归正题

如果一次走三步:

有一种极限情况,假如你这个环是奇数,那么你和他的距离(3-1=2)每次缩短两步,一个奇数无论减去多少个偶数,依然是个奇数,你们不会相遇,有的只是无穷的错过

如果一次四步:

如果这个环是偶数,那么你和它的距离是以每次(4-1=3)三个距离的位置缩短,如果这个环是偶数,它每次距离减 3,(在极端条件下)无论减多少次他都不会相遇。

如果每次走两步:

不管奇数偶数,由于我每次走两步,我们直接的距离每次缩短(2-1=1)1步,所有的整数都是1的倍数,我们终会相遇!

4.找到环形链表的入口(进阶)

在这里插入图片描述

题目:判断是否是循环链表,如果该链表带环则返回进入环的第一个结点.

快慢指针解法

这个其实就涉及到数学的知识了,我们依旧是画图举例。

在这里插入图片描述

我们此时就可以认为L是没进入环时的长度,X的长度是从进入环的第一个结点开始计算直到快慢指针相交时的距离。C是环内剩下的长度。

慢指针一次走一步,快指针一次走两步

所以我们可以推导出

慢指针走的的长度:L+x
快指针走的长度:L+x+k(x+c)
x+c=环的周长

因为可能会存在,很短,L很长的这种情况,如果是这种情况,那么快指针就需要重复走好多遍这个,所以就是k(x+c)

通过上面的公式可以算出:

由于快指针的速度是慢指针的二倍,所以当慢指针 × 2才是快指针走的距离

2(L+x)=L+k(x+c)+x
//下面是去括号,移项,合并同类型
L+x=x+c+(k-1)(x+c)
L=c+(k-1)(x+c)
L-(k-1)(x+c)=c

我们假设两个结点A,B如图

在这里插入图片描述

不管我的A指针走了多少个(x+c)我的A指针的位置依旧没变,但是我的B指针却往前移动了(k-1)(x+c)个位置,所以,当走完了(k-1)(x+c)时,我们的B距离环形入口只剩下c个结点,又因为我们的A指针距离环的入口也是c个结点,所以他们相交的位置就是环的入口。

我们不能判断这些变量究竟为几,但是我们通过查找他们直接的关系,就可以找到正确答案!

图片如下:

第一步:找到交点

在这里插入图片描述

第二步:两个指针一个从头变量,一个从相交结点开始

在这里插入图片描述

代码如下:

public class Solution {
    public ListNode detectCycle(ListNode head) {
        
        ListNode A=head;//慢指针
        ListNode B=head;//快指针
        //找到交点
        while(B!=null&&B.next!=null) {
				//①
            B=B.next.next;
            A=A.next;
            //切记不可以写在①那个位置
            if(A==B) {
                break;
            }
        }
        //判断因为什么退出循环
        //如果是因为结点为空,则直接返回null
        if(B==null||B.next==null) {
            return null;
        }
        //
        B=head;//B指针是头节点的位置
        //相交时,他们的交点就是环的入口结点
        while(A!=B) {
            A=A.next;
            B=B.next;
        }
        //返回A,B都行
        return A;
    }
}

注意:

注释中提到了,if()不可以写在①的那个位置,因为他们开始时本身就是相同的,如果写在了一起,那么就会发生直接退出循环!!!

复杂度分析:

  • 时间复杂度:O(N),其中 N 为链表中节点的数目。在最初判断快慢指针是否相遇时,prev指针走过的距离不会超过链表的总长度;随后寻找入环点时,走过的距离也不会超过链表的总长度。因此,总的执行时间为 O(N)+O(N)=O(N)

  • 空间复杂度:O(1)。我们只使用了 A B 2个指针。

5.相交链表

在这里插入图片描述

题目

给你两个链表,判断是否相交,若相交,返回相交结点!

解法一:暴力解法

上面链表的每一个结点都和下面的整个链表进行对比,最坏情况下,如果两个链表不相交,时间复杂度是O(n^2),所以这里就不演示了,我们讲解时间复杂度为O(n)的解法

解法二:双指针遍历

思路:

我们可以定义两个指针,分别从两个链表的头开始遍历,然后找到求出链表的差值,长的链表走完差值,两个链表一起向后遍历,相同时退出,返回相同的结点。

具体步骤:

1.定义两个指针,分别从两个链表的头开始向后遍历
2.记录两个链表的长度
3.求出链表长度的差值
4.长的链表先走完差值
5.两个链表再同时向后遍历,直到相同时退出

如图:

第一步:遍历相交链表

在这里插入图片描述
第二步:长链表先走完差值,再一起走

在这里插入图片描述

代码如下

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
		// 一个链表为空永远不可能香蕉
        if (headA==null&&headB==null) {
            return null;
        }
        //分别记录headA headB
        ListNode A= headA;
        ListNode B=headB;
        //用来记录链表长度
        int count=0;
        //用来记录最短长度
        int sum=0;
        //用来判断哪个链表长
        boolean p=false;
        //两个链表一起走,有一个为空时,最短长度记录完成
        while (A!=null&&B!=null) {
            A=A.next;
            B=B.next;
            count++;
            sum++;
        }
        //继续走长的链表,直到长链表也走完,记录长链表的长度
        
        while (A!=null) {
            A=A.next;
            count++;
            //如果是A链表不为空,那么p为true
            p=true;
        }
        //同上
        while (B!=null) {
            B=B.next;
            //如果是B链表不为空则p不变
            count++;
        }
        //重新找到头
        A=headA;
        //同上
        B=headB;
        //让长的先走
        int n=count-sum;
        //P为真A先走n步
        if (p) {
            while (n>0) {
                A=A.next;
                n--;
            }//否则B先走
        } else {
            while (n>0) {
                B=B.next;
                n--;
            }
        }//走完链表的差值后,两个链表一起遍历
        //香蕉退出循环
        while (A!=B) {
            A=A.next;
            B=B.next;
        }
        return A;
    }
}

复杂度分析:

  • 时间复杂度:O(N),总共遍历了一次完成的长链表外加找交点时遍历了一次链表,总共遍历了2*n次(n代表最长链表的长度)

  • 空间复杂度:O(1),

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/63923.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于冲突搜索的多机器人路径规划(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 随着自动化物流系统的发展,移动机器人作为运输系统的关键工具,各方面的技术得到了快速的发展。多移动机器人路径规划是机器人导…

什么是单臂路由技术?

使用vlan trunk可以在同一交换机上划分vlan&#xff0c;从而相同vlan的主机可以通信&#xff0c;不同vlan不能通信&#xff0c;如果连接不同vlan的主机想要通信&#xff0c;怎么办&#xff1f; 不同vlan是属于不同广播域的&#xff0c;配置的是不同的IP网段&#xff0c;针对不…

Notepad++官网地址及使用十六进制查看文件的详细教程

目录 一、概述 二、Notepad官网地址 三、Notepad查看十六进制教程  3.1 在线安装HexEditor插件  3.2 手动安装HexEditor插件 一、概述 Notepad是一个开源的源代码编辑器和记事本替代品&#xff0c;支持多种编程语言和自然语言。在MS Windows环境下运行&#xff0c;它的使用受…

在线数据图表制作-FineReport文本控件

1. 概述 1.1 版本 报表服务器版本 App 版本 功能变更 11.0 -- -- 11.0.2 11.0.54 App支持文本控件 NFC 读卡输入 1.2 应用场景 1.2.1 填报控件 填报报表中可以通过该控件输入文本信息&#xff0c;录入填报的数据&#xff0c;如下图所示&#xff1a; 1.2.2 参数控件 …

51单片机APP GSM短信老人跌倒定位温度异常报警检测GPS地图

实践制作DIY- GC0088-跌倒定位温度异常报警 一、功能说明&#xff1a; 基于STM32单片机设计-跌倒定位温度异常报警 功能介绍&#xff1a; STC15W4K48S4&#xff08;或者STM32F103CxT6&#xff09;系列最小系统板OLED显示器SIM800 GSM短信模块1个DS18B20温度测量模块蜂鸣器AD…

我的世界杯 - 诸神黄昏之战

话说长这么大还是头一回主动的去看世界杯比赛... 小时候只是我爷爷他们喜欢看 【CCTV-5】 各种球的比赛&#xff0c;而我我对此毫无兴趣可言&#xff0c;每天只要有时间就沉醉在属于我的 【少儿频道】&#xff0c;喜羊羊、葫芦娃、红猫蓝兔、小娜扎... 而这次可能是因为朋友圈的…

Spring【Bean的作用域与生命周期】

Spring【Bean的作用域与生命周期】&#x1f34e;一.Bean作用域问题&#x1f352;1.1 被修改的 Bean 案例&#x1f352;1.2 原因分析&#x1f34e;二.作⽤域定义&#x1f352;2.1Bean 的 6 种作⽤域&#x1f349; 2.1.1singleton(单例作⽤域)&#x1f349; 2.1.2prototype(原型作…

微服务守护神-Sentinel-概念

引言 书接上篇 微服务应对雪崩的容错方案 &#xff0c;大概知道微服务容错方案可以从隔离、超时、限流、熔断、降级这几方面入手。好了&#xff0c;理论了解了&#xff0c;那代码落地方案&#xff1f;这是后本篇的主角要登陆场&#xff1a;Sentinel&#xff0c;微服务的守护神…

Xylan-MAL|木聚糖-马来酰亚胺|木聚糖-聚乙二醇-马来酰亚胺|马来酰亚胺-PEG-木聚糖

Xylan-MAL|木聚糖-马来酰亚胺|木聚糖-聚乙二醇-马来酰亚胺|马来酰亚胺-PEG-木聚糖 中文名称&#xff1a;木聚糖-马来酰亚胺 英文名称&#xff1a;Xylan-MAL 别称&#xff1a;马来酰亚胺修饰木聚糖&#xff0c;马来酰亚胺-木聚糖 存储条件&#xff1a;-20C&#xff0c;避光&…

编程初学者应该先学C++、Java还是Python?

语言推荐&#xff1a; 第一大类语言包括Java、C、Python和C. 这类语言都是非常通用的语言,它们并不局限于特定的编程平台或用途。(无疑问&#xff0c;你应该熟悉这四种语言。) 第二大类语言包括Java、C#、PHP和Swift。 Java 和PHP是主要的Web开发语言。C# 是微软的编程语言&…

总结《你不知道的JavaScript》三卷小记

先讲一些废话 三本小黄书&#xff0c;工作日都抽出半小时来看看&#xff0c;三本书加来一共700多页的样子。我拖拖延延看了三个月&#xff0c;终于看完了。然后现在抽出时间随便写点&#xff0c;我只是挑一些自己想总结的&#xff0c;给自己一个名词概念&#xff0c;好了废话就…

FreeRTOS移植

FreeRTOS移植一、获取FreeRTOS源码1.1 官网下载1.2 源码路径二、移植2.1 工程内新建分支2.2 分支内添加文件FreeRTOS_COREFreeRTOS_PORTABLE添加完成2.3 添加 FreeRTOSConfig.h 文件2.4 添加 FreeRTOS 头文件路径三、举例3.1 包含头文件3.2 创建任务四、FreeRTOSConfig.h 附录一…

Redis——(7)redis作为mybatis缓存整合二级缓存的整合

1.作为mybits的缓存整合 1&#xff09;用户第一次访问的时候获取数据库的值&#xff0c;再次访问时直接从缓存中获取数据 2&#xff09;设置缓存过期时间 3)项目8080端口是对外端口&#xff08;向外部暴露的端口&#xff09;&#xff0c;区别于内部进程号,查内部端口用ps -ef|…

分布式环境下Spring Session Redis底层原理

1 自动装配 public class SessionAutoConfiguration {// SessionRepositoryFilterConfiguration用来配置核心的过滤器// 3 核心过滤器Configuration(proxyBeanMethods false)ConditionalOnWebApplication(type Type.SERVLET)Import({ ServletSessionRepositoryValidator.clas…

什么是代码签名证书?

使用代码签名证书&#xff0c;您可以保证签名者的身份和软件的完整性&#xff0c;这可以防止在下载和安装软件时出现警告。 代码签名证书是软件开发人员用来签署其软件、应用程序和驱动程序代码的数字证书。它使用公私密钥基础设施(PKI)将实体绑定到公钥和私钥。 申请代码签名…

SuperKnob超级旋钮

SuperKnob超级旋钮 2022年12月5日&#xff0c;今天总结下几个月前搞得小项目&#xff0c;好久没有写文总结了&#xff0c;有多少人不再写博客了呢&#xff1f;转眼疫情已三年&#xff0c;生活节奏太快了&#xff0c;认识的很多大佬他们的博客也不再更新了&#xff0c;都在努力…

Vue3 组件,一点也不难

Vue3 组件&#xff0c;一点也不难1.简介2.一个简单的 Vue 组件的实例3.局部组件4.Prop5.动态 Prop1.简介 组件是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素&#xff0c;封装可重用的代码。 组件系统让我们可以用独立可复用的小组件来构建大型应用&#xff0c;几乎任意…

jmeter压测mysql(保姆级教程)

准备工作&#xff08;下载mysql-connector&#xff09;&#xff1a; 打开mysql官网&#xff1a;MySQL 找到mysql connectors&#xff0c;选择操作系统独立版本&#xff0c;将下载jar包导入到jmeter的lib目录下。 参考文档&#xff1a;https://www.jb51.net/article/190860.h…

安装包UI美化之路-nsNiuniuSkin多语言配置

今天给大家带来的多语言配置功能&#xff0c;算是nsNiuniuSkin的一次质的提升&#xff0c;希望对大家有所帮助。 nsNiuniuSkin通过自研多语言配置引擎&#xff0c;让安装包的多语言配置不再复杂&#xff0c;直接设置语言翻译配置文件&#xff0c;即可实现安装包整体的多语言切…

一文读懂机智云物联网APP开发

本教程可用于C2 DevKit开发板套件全功能展示&#xff0c;用于对shineblink 的C2 DevKit开发套件全面学习使用&#xff0c;shineblink&及机智云技术交流群&#xff08;234976524&#xff09;。开发板相关的进阶教程及DIY应用将在后续持续更新。教程主要包含以下内容&#xf…