【Java数据结构】详解LinkedList与链表(二)

目录

1.❤️❤️前言~🥳🎉🎉🎉

2.反转一个单链表 

3. 找到链表的中间节点

4.输入一个链表,输出该链表中倒数第k个结点。 

  5.合并两个有序链表

6.链表分割 

7. 判定链表的回文结构

8.输入两个链表,找出它们的第一个公共结点。 

9. 判断链表中是否有环

10.返回链表开始入环的第一个节点

 11.总结​​​​​​​


1.❤️❤️前言~🥳🎉🎉🎉

Hello, Hello~ 亲爱的朋友们👋👋,这里是E绵绵呀✍️✍️。

如果你喜欢这篇文章,请别吝啬你的点赞❤️❤️和收藏📖📖。如果你对我的内容感兴趣,记得关注我👀👀以便不错过每一篇精彩。

当然,如果在阅读中发现任何问题或疑问,我非常欢迎你在评论区留言指正🗨️🗨️。让我们共同努力,一起进步!

加油,一起CHIN UP!💪💪

🔗个人主页:E绵绵的博客
📚所属专栏:

1. JAVA知识点专栏

        深入探索JAVA的核心概念与技术细节

2.JAVA题目练习

        实战演练,巩固JAVA编程技能

3.c语言知识点专栏

        揭示c语言的底层逻辑与高级特性

4.c语言题目练习

        挑战自我,提升c语言编程能力

📘 持续更新中,敬请期待❤️❤️ 

这篇文章我们将给大家带来一些单链表的面试题,都很有意思,来看一下吧。

2.反转一个单链表 

  给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。


我们的解决方法是依次头插法:

最开始时我们需要将第一个节点的next值变为null,使其变成最后的节点,就产生了新的链表。而后依次将原始链表中的第二个节点,第三个节点直至最后一个节点头插到新链表中。

这样就翻转成功了

完整代码如下: 

public void reverseList() {
        if(head==null)
            return;
        ListNode cur=head.next;
        ListNode prev;
        head.next=null;
        while(cur!=null){
            prev=cur.next;
            cur.next=head;
            head=cur;
            cur=prev;
        }
    }

这是该题的链接 : 翻转链表                                    

3. 找到链表的中间节点

   给你单链表的头结点 head ,请你找出并返回链表的中间结点。

   如果有两个中间结点,则返回第二个中间结点。


该题的思路实现是设置两个引用:slow 慢引用 和 fast 快引用 

fast 和 slow 同时从head开始遍历,但是fast一次走两步,slow一次只走一步,最后我们会发现当 fast遍历完成后 ,对应的slow正好是链表的中间节点或者第二个中间节点。

fast遍历完成的标志是fast.next=null或者fast=null

  完整代码如下:

public ListNode middleNode(ListNode head) {
     ListNode fast=head;
   ListNode  slow=head;
    while(fast!=null&&fast.next!=null){
           fast=fast.next.next;
           slow=slow.next;
    }
        return slow;
}

该题链接:求链表中间结点 

4.输入一个链表,输出该链表中倒数第k个结点。 



该题有两种思路

 第一种思路: 


第二种思路:


所以我们采用第二种思路去做题,且我们还需要注意k的合法性。

整体代码如下:

public ListNode FindKthToTail(ListNode head,int k){
if(head==null){
return null;
}
ListNode fast = head;
ListNode slow = head;
//1.判断k值的合法性
if( k<=0 ){
System.out.println("k值不合法");
return null;
}
//2.让fast 提前走 k-1 步
while(k-1>0){
if(fast == null |l fast.next ==null){
System.out.println("k值不合法”);
return null;
}
fast = fast.next;
k--;
}
while(fast !=null && fast.next != null){
fast = fast.next;slow = slow.next;
return slow;
}
}

该题题目已下架,无链接。



  5.合并两个有序链表


将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 

 解题思路:

1.先创建一个新的链表

2.让cur1 和 cur2遍历两个链表,遇到的节点逐个比较,将值小的节点往新链表的末尾tail进行尾插

3.上述循环结束后,要判断有无链表还有剩余元素,把剩余的元素尾插到新链表的末尾。

  完整代码如下:

public  static ListNode mergeTwoLists(ListNode list1, ListNode list2){
        //将两个有序链表合并为一个有序链表
        ListNode head1=list1;
        ListNode head2=list2;
        ListNode head=new ListNode(-1);
        ListNode cur=head;
        while(head1!=null&&head2!=null){
            if(head1.value>head2.value){
                cur.next=head2;
                cur=cur.next;
                head2=head2.next;
            }
            else{
                cur.next=head1;
                cur=cur.next;
                head1=head1.next;
            }
        }
        while(head1!=null){
            cur.next=head1;
            cur=cur.next;
            head1=head1.next;
        }
        while(head2!=null){
            cur.next=head2;
            cur=cur.next;
            head2=head2.next;
        }

        return head.next;
    }

  该题链接:合并两个有序链表

6.链表分割 

现有一链表的头引用 pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。

   解题思路:

1.创建两个链表,一个链表尾插值小于x的节点,另一个链表中插值大于等于x的节点

2.遍历原链表,将链表中的节点往两个新链表中尾插

3.将两个链表拼接

但是这种思路仍然存在较大的问题,什么问题呢?


1.我们传入的x值比节点中val都大或者都小,那么存在一个问题就是有一个链表中的内容为空,那么按照上面的思路走时,必然会出现空指针异常的情况。

解决方法:  当第一个区间为空时,那么直接返回ca


2.最后一个节点分到小于x的区间,那么最后一个节点的next并不是null,所以此时我们必须手动将 cb.next=null; 预防最后一个节点的next不是null,从而报错。 

完整代码如下:

  public static ListNode partition(ListNode pHead, int x) {
        //给一定值x,该方法将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。
        if(pHead==null)
            return  null;
        ListNode cur=pHead;
        ListNode  sa=null;ListNode  sb=null;   //小于x的结点所在链表
        ListNode  ca=null;ListNode  cb=null; //大于x的结点所在链表
        while(cur!=null){
            if(cur.value<x){
                if(sa==null){
                    sa=cur;
                    sb=cur;
                    cur=cur.next;
                }else{
                    sb.next=cur;
                    sb=cur;
                    cur=cur.next;
                }
            }else{
                if(ca==null){
                    ca=cur;
                    cb=cur;
                    cur=cur.next;
                }else{
                    cb.next=cur;
                    cb=cur;
                    cur=cur.next;
                } }
        }
        //拼接两个链表
        if(sa==null)
            return  ca;
        //如果不存在小于X的结点,则直接返回大于x的链表,否则按如下执行会报错
        if(ca!=null)
            cb.next=null;
          //将链表中的最后一个结点的next值变为null,防止其循环从而报错
        sb.next=ca;
        return sa;
    }

题目链接:链表分割_牛客题霸_牛客网

7. 判定链表的回文结构



解题思路实现:

1.找到链表的中间节点,找中间节点:采用快慢指针就行了
2.对链表的后半部分进行反转

3.将两个链表从前往后逐个节点进行比对,如果比对之后发现所有节点的值域都相同,则是回文链表,否则不是.

并且在循环时还需注意当链表为奇数或偶数时,判定循环结束的标志不同:

 奇数是head==slow时结束

 偶数是head.next=slow时结束

所以完整代码如下:


public static  boolean checkPalindrome(ListNode A) {
        //判断链表是否为回文结构
        if(A==null||A.next==null)
            return true;
        ListNode slow=A;
        ListNode fast=A;
        while(fast!=null&&fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
        }
        ListNode cur=slow.next;
        while(cur!=null){
            ListNode  curnext=cur.next;
            cur.next=slow;
            slow=cur;
            cur=curnext;
        }
        ListNode head=A;
        while(head!=slow){
            if(head.value!=slow.value)
                return false;
            if(head.next==slow)
                return true;
            head=head.next;
            slow=slow.next;
        }
        return true;
    }

注意:在执行完该方法后链表结构会被破坏掉,之后如果还对该链表进行操作,结果会和我们预想的结果不一样。

该题链接:链表的回文结构_牛客题霸_牛客网 

8.输入两个链表,找出它们的第一个公共结点。 

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。 


下面是两个节点相交的各类情况:


从上述链表相交的情况看出,两个单链表只要相交,从交点开始,到其后续所有的节点都是两个链表中公共的节点
检测两个链表是否相交:分别找到两个链表中的最后一个节点,然后检测这两个节点的地址是否相同即可:如果地址相同则相交,否则不相交

解题思路:

1.让较长的链表先走两个链表的差值步数

2.再让两个链表同时往后走,直到遇到地址相同的交点,没有则返回null

  public static  ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //给你两个单链表的头节点 headA 和 headB ,找出并返回两个单链表相交的起始节点。
            if(headA==null||headB==null)
                return null;

            ListNode cur1=headA;
            ListNode cur2=headB;
            int length1=0;
            int length2=0;
            while(cur1!=null){
                length1++;
                cur1=cur1.next;
            }while(cur2!=null){
                length2++;
                cur2=cur2.next;
            }
            cur1=headA;
            cur2=headB;
            if(length1>length2){
                for(int i=0;i<length1-length2;i++){
                    cur1=cur1.next;
                }
            }else{
                for(int i=0;i<length2-length1;i++){
                    cur2=cur2.next;
                }
            }
            while(cur1!=null){
                if(cur1==cur2)
                    return cur1;
                cur1=cur1.next;
                cur2=cur2.next;
            }
            return null;
        }

题目链接:找出两个链表的第一个公共节点

9. 判断链表中是否有环

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


解题思路:

设计快慢指针去解决,即慢指针一次走一步,快指针一次走两步,两个指针从链表起始位置开始运行,如果链表带环则一定会在环中相遇,否则快指针率先走到链表的末尾。

扩展问题:

1.为什么快指针每次走两步,慢指针走一步可以?

假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚进环时,可能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。此时,两个指针每移动一次,之间的距离就缩小一步,不会出现始终相遇不到的情况,因此:在慢指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。

2.快指针一次走3步,走4步,...n步行吗?

假设:快指针每次走3步,满指针每次走一步,此时快指针肯定先进环,慢指针后来才进环。假设慢指针进环时候,快指针的位置如图所示:

此时按照上述方法来绕环移动,每次快指针走3步,慢指针走1步,它们是永远不会相遇的,因此不行。
只有快指针走2步,慢指针走一步才可以,因为每移动一次,它们之间的距离就缩小一步,无论如何都能相遇。

所以我们在环问题中设置快慢指针,都是将快指针设置为一次两步,慢指针一次一步,这样当存在圈时无论如何都会相遇。

    完整代码如下:

 public static  boolean hasCycle(ListNode head) {
        //给你一个链表的头节点 head ,判断链表中是否有环。
        if(head==null||head.next==null)//只有一个节点时默认绝对不存在环
            return false;
        ListNode slow=head;
        ListNode  fast=head;
        while(fast!=null&&fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(slow==fast)
                return true;
        }
        return false;
    }

题目链接 :判断链表中是否有环

10.返回链表开始入环的第一个节点

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

解题思路:

设计一个慢指针,一个快指针,快指针一次两步,慢指针一次一步。使两指针相遇而后让一个指针从链表起始位置开始遍历链表,同时也让一个指针从相遇点的位置开始绕环运行,两个指针都是每次均走一步,最终肯定会在入口点的位置相遇。  

它们肯定会在入口点相遇的证明如下:

 


上图中的H为链表的起始点,E为环入口点,M为慢速指针相遇点,设环的长度为R,H到E的距离为L ,E到M的距离为X,则M到E的距离为R-X
在快慢指针相遇过程时,快慢指针相遇时所走的路径长度:
fast:L +X + nR      slow:L+ X
注意:

1.当慢指针进入环时,快指针可能已经在环中绕了n圈了,n至少为1。

因为:快指针先进环走到M的位置,最后又在M的位置与慢指针相遇
2.慢指针进环之后,快指针肯定会在慢指针走一圈之内追上慢指针

因为:慢指针进环后,快慢指针之间的距离最多就是环的长度,而两个指针在移动时,每次它们至今的距离都缩减一步,因此在慢指针移动一圈之前快指针肯定是可以追上慢指针的
而快指针速度是满指针的两倍。

所以有如下关系是:

2 *(L+X)=L+X+nR

L+X=nR

L=nR-X(n为1,2,3,4..……,n的大小取决于环的大小,环越小n越大)
极端情况下,假设n=1,此时:L=R-X


所以由此得知一个指针从链表起始位置运行,一个指针从相遇点位置绕环,每次都走一步,两个指针无论如何最终都会在入口点的位置相遇。

   完整代码如下:

public static  ListNode detectCycle(ListNode head) {
        //给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
        if(head==null||head.next==null)
            return null;
        ListNode slow=head;
        ListNode  fast=head;
        while(fast!=null&&fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(slow==fast){
                slow=head;
                while(slow!=fast){
                    slow=slow.next;
                    fast=fast.next;
                }
                return slow;
            }
        }
        return null;
    }

 11.总结

所以对于这10个面试题我们就讲述清楚了,并且我还把其中的部分题目当作特殊方法加入到模拟的无头单向非循环链表类中。

对于该无头单向非循环链表的模拟实现和其具体使用放到码云里了,大家可以看下:无头单向非循环链表的模拟实现和其具体使用(此外还往模拟的链表内部添加了一些特殊方法)

下篇文章将给大家带来LinkedList的模拟实现和具体使用。

在此,我们诚挚地邀请各位大佬们为我们点赞、关注,并在评论区留下您宝贵的意见与建议。让我们共同学习,共同进步,为知识的海洋增添更多宝贵的财富!🎉🎉🎉❤️❤️💕💕🥳👏👏👏 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java 代码审计---反序列化

Java 序列化是一种将对象转换为字节流的过程&#xff0c;以便可以将对象保存到磁盘上&#xff0c;将其传输到网络上&#xff0c;或者将其存储在内存中&#xff0c;以后再进行反序列化&#xff0c;将字节流重新转换为对象。 序列化在 Java 中是通过 java.io.Serializable 接口来…

C++候捷stl-视频笔记2

深度搜索list list是双向链表&#xff1a;底部实现是环状双向链表 list内部除了存data之外&#xff0c;还要存一个前向指针prev和一个后向指针next list的iterator&#xff0c;当迭代器的时候&#xff0c;是从一个节点走到下一个节点&#xff0c;是通过访问next指针实现的 主要…

C语言:深入了解(联合体和枚举)

目录 联合体 联合体的类型的声明 联合体的特点 相同成员的结构体和联合体对比 联合体大小的计算 联合体的使用举例 联合体的类型&#xff1a;判断联合体是大端还是小端 枚举类型 枚举类型声明 枚举类型的优点 枚举类型的使用 联合体 联合体的类型的声明 像结构体⼀…

(含笔试题)深度解析数据在内存中的存储

目录 本章重点 前言&#xff1a; 1.整型在内存中的存储 1.1原码、反码、补码 原码 反码 补码 2.大小端字节序介绍 什么是大小端字节序&#xff1a; 为什么会有大小端字节序&#xff1a; 3.浮点数存储规则 本章重点 1. 整形在内存中的存储&#xff1a;原码、反码…

刷机 iPhone 进入恢复模式

文章目录 第 1 步&#xff1a;确保你有一台电脑&#xff08;Mac 或 PC&#xff09;第 2 步&#xff1a;将 iPhone 关机第 3 步&#xff1a;将 iPhone 置于恢复模式第 4 步&#xff1a;使用 Mac 或 PC 恢复 iPhone需要更多协助&#xff1f; 本文转载自&#xff1a;如果你忘记了 …

【嵌入式硬件】DRV8874电机驱动

目录 1 芯片介绍 1.1 特性简介 1.2 引脚配置 1.3 最佳运行条件 2 详细说明 2.1 PMODE配置控制模式 2.1.1 PH/EN 控制模式 2.1.2 PWM 控制模式 2.1.3 独立半桥控制模式 2.2 电流感测和调节 2.2.1 IPROPI电流感测 2.2.2 IMODE电流调节 3.应用 3.1设计要求 3.2 设计…

逆天工具一键修复图片,视频去码。本地部署超详细!!

上一篇文章&#xff1a;逆天工具一键修复图片&#xff0c;视频去码。简直不要太好用&#xff01;-CSDN博客 根据上一篇文章展示的效果&#xff0c;本文章主要讲如何部署本地github开源项目。博主走了无数弯路&#xff0c;最后精化下来的步骤&#xff0c;超级详细&#xff01;&a…

统计信号处理基础 习题解答10-5

题目 通过令 并进行计算来重新推导MMSE估计量。提示&#xff1a;利用结果 解答 首先需要明确的是&#xff1a; 上式是关于观测值x 的函数 其次需要说明一下这个结果 和教材一样&#xff0c;我们用求期望&#xff0c;需要注意的是&#xff0c;在贝叶斯情况下&#xff0c;是个…

Amis源码 embed渲染方法解析(json结构渲染原理):

js sdk中的渲染函数embed使用方式如下&#xff1a; const amis amisRequire("amis/embed"); const amisScoped amis.embed( self.$refs["mnode"],amisJSON, {}, amisEnv); //env会有默认值&#xff0c;默认值与传来的参数进行合并&#xff08;{默认值…

【学习Day5】操作系统

✍&#x1f3fb;记录学习过程中的输出&#xff0c;坚持每天学习一点点~ ❤️希望能给大家提供帮助~欢迎点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;指点&#x1f64f; 学习编辑文章的时间不太够用&#xff0c;先放思维导图&#xff0c;后续复习完善细节。

【每日刷题】Day53

【每日刷题】Day53 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 1019. 链表中的下一个更大节点 - 力扣&#xff08;LeetCode&#xff09; 2. 116. 填充每个节点的下一…

mac多媒体影音库:Emby for Mac 中文版

Emby软件是一款功能强大的媒体服务器软件&#xff0c;旨在为用户提供丰富的多媒体体验。以下是关于Emby软件的详细介绍&#xff1a; 下载地址&#xff1a;https://www.macz.com/mac/7964.html?idOTI2NjQ5Jl8mMjcuMTg2LjE1LjE4Mg%3D%3D 主要功能 媒体管理&#xff1a;Emby允许用…

python编程:SQLite 管理图片数据库

在本博客中&#xff0c;我们将介绍如何使用 wxPython 和 sqlite3 模块构建一个 GUI 应用程序&#xff0c;该程序可以遍历指定文件夹中的所有图片&#xff0c;并将其信息存储到 SQLite 数据库中。 C:\pythoncode\new\InputImageOFFolderTOSqlite.py 项目简介 我们的目标是创建…

新版校园跑腿外卖独立版+APP+小程序前端外卖配送平台源码

同城校园跑腿外卖配送平台源码&#xff0c;这套目前全网还没有人分享过&#xff0c;这个是开源的&#xff0c;所以没有任何问题了&#xff0c;这套源码非常吊&#xff0c;支持自定义diy 你可以设计你的页面&#xff0c;设计你自己的风格&#xff0c;支持多校园&#xff0c;独立…

Java基础入门day62

day62 AJAX 概念 AJAX&#xff1a; Asynchronous Javascript And XML AJAX是一种无需重新加载整个网页的情况下&#xff0c;能够更新部分网页的技术 AJAX是一种用于创建快速动态网页的技术 通过在后台与服务器进行少量数据交换&#xff0c;AJAX可以使网页实现异步更新 传统…

Jvm(二)新生代和老年代与GC回收

目录 新生代和老年代 新生代 MinorGC 老年代&#xff08;Old Generation&#xff09; MajorGC Minor GC、Major GC 和 Full GC 三个GC具体区别和使用场景 JVM GC及内存调优的参数 调优建议 前言-与正文无关 ​ 生活远不止眼前的苦劳与奔波&#xff0c;它还充满了无…

【教程】自监督 对比学习,代码,爽学一波

from&#xff1a; https://docs.lightly.ai/self-supervised-learning/examples/simclr.html

1114 全素日

你好哇&#xff0c;新的一天开始啦&#xff01; solution 取数值的不同部分&#xff0c;联想到借助string #include<iostream> #include<string> using namespace std; bool judge(string s){int n atoi(s.c_str());if(n 1 || n 0) return false;for(int i 2…

基于51单片机的超声波测距—数码管显示

基于51单片机的超声波测距 &#xff08;仿真&#xff0b;程序&#xff0b;原理图&#xff0b;PCB&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.HC-SR04模块测量距离&#xff0c;LED数码管显示距离&#xff1b; 2.测量范围&#xff1a;2cm-400cm&…

深度学习中的模型架构详解:RNN、LSTM、TextCNN和Transformer

深度学习中的模型架构详解&#xff1a;RNN、LSTM、TextCNN和Transformer 文章目录 深度学习中的模型架构详解&#xff1a;RNN、LSTM、TextCNN和Transformer循环神经网络 (RNN)RNN的优点RNN的缺点RNN的代码实现 长短期记忆网络 (LSTM)LSTM的优点LSTM的缺点LSTM的代码实现 TextCN…