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

news2024/11/20 13:30:02

目录

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的模拟实现和具体使用。

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

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

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

相关文章

【Centos7】解决 CentOS 7 中出现 “xx: command not found“ 错误的全面指南

【Centos7】初探xx:command not found解决方案 大家好 我是寸铁&#x1f44a; 【Centos7】解决 CentOS 7 中出现 “xx: command not found” 错误的全面指南✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 经常有小伙伴问我&#xff0c;xx:command not found怎么办&#xff1…

超实惠的GPU云服务器安利!!

自己一个人抱着老笔记本学深度学习&#xff0c;没有GPU是真的难受。Colab用过&#xff0c;GPU稍微用用就被剥夺了。华为云在培训的时候也用过&#xff0c;好贵。现在学到大模型&#xff0c;cuda10.1举步维艰。 失眠在网上冲浪&#xff0c;刷到了潞晨云&#xff0c;一块六就能用…

强烈安利10款手机App!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/ 1.听书神器——昊昊听书 昊昊听书app是一款专门为用户提供有声读物的应用程序。它不仅提供了各种类型的有声书籍&#xff0c;还有各种知名的电…

2024年6月2日 (周日) 叶子游戏新闻

中医百科中药: 中医百科中药是一款非常强大的中药知识科普软件&#xff0c;该应用提供500多味中草药的文献资料&#xff0c;强大的搜索功能可根据功效、特点和关键词来快速查找中药&#xff0c;而且每味中药的图片、功效、主治、炮制方法等百科知识&#xff0c;可以很好的帮助你…

使用 Logback.xml 配置文件输出日志信息

官方链接&#xff1a;Chapter 3: Configurationhttps://logback.qos.ch/manual/configuration.html 配置使用 logback 的方式有很多种&#xff0c;而使用配置文件是较为简单的一种方式&#xff0c;下述就是简单描述一个 logback 配置文件基本的配置项&#xff1a; 由于 logba…

王道408数据结构CH4_串

概述 4 串 4.1 串的实现 4.1.1 存储结构 定长顺序存储 #define Maxsize 255typedef struct{char *ch[Maxsize];int length; }SString;堆分配存储 typedef struct{char *ch;int length; }HString;块链存储 4.1.2 基本操作 4.2 模式匹配&#xff08;子串定位&#xff09; 4.2.…

单元测试的心法分享

大家好&#xff0c;我是G探险者&#xff01; 今天我们简单聊聊单元测试的哪些事儿~ 两天时间我玩明白了单元测试的套路。 这里我分享一下思路。 在我眼里单元测试室什么&#xff1f; 请看这张草图&#xff1a; 单元测试主要关注单个代码单元&#xff08;通常是类或方法&am…

小熊家务帮day13-day14 门户管理(ES搜索,Canal+MQ同步,索引同步)

目录 1 服务搜索1.1 需求分析1.2 技术方案1.2.1 使用Elasticsearch进行全文检索&#xff08;为什么数据没有那么多还要用ES&#xff1f;&#xff09;1.2.2 索引同步方案1.2.2.1 Canal介绍1.2.2.1 Canal工作原理 1 服务搜索 1.1 需求分析 服务搜索的入口有两处&#xff1a; 在…

Threejs加载DOM+CSS到场景中,实现3D场景展示2D平面的效果

1. 前言 本篇文章主要实现了将DOM元素转换为Threejs可以使用的数据结构,使用CSS2DRenderer渲染器渲染这些DOMCSS的平面,使其可以作为一个物体添加到Threejs场景里 如下效果图: 2. 实现步骤 首先创建一个ThreejsVueVite的项目,作为本次的demo项目下载Threejs第三方库 yarn…

信息学奥赛初赛天天练-20-完善程序-vector数组参数引用传递、二分中值与二分边界应用的深度解析

PDF文档公众号回复关键字:20240605 1 2023 CSP-J 完善程序1 完善程序&#xff08;单选题&#xff0c;每小题 3 分&#xff0c;共计 30 分&#xff09; 原有长度为 n1,公差为1等升数列&#xff0c;将数列输到程序的数组时移除了一个元素&#xff0c;导致长度为 n 的开序数组…

[Moveith控制问题]:Failed to fetch current robot state报错分析及解决办法

问题描述&#xff1a; 在使用Moveit获取机械臂关节角度时&#xff0c;有时会遇到如下错误信息&#xff1a; 原因分析&#xff1a; 出现这一错误的原因主要在于Moveit的状态监视器在处理回调函数 planning_scene_monitor::CurrentStateMonitor::jointStateCallback 中传入的联合…

【EFK日志系统】docker一键部署filebeat、metricbeat

docker一键部署filebeat、metricbeat filebeat部署创建配置文件一键启动修改配置文件查验信息 metricbeat部署创建配置文件一键启动修改配置文件查验信息 上两篇文章写了搭建部署es集群和部署kibana 这篇写一键部署filebeat和metricbeat收集工具 规划服务器是 es01:172.23.16…

HarmonyOS(二十四)——Harmonyos通用事件之触摸事件

1.触摸事件。 触摸事件是HarmonyOS通用事件的一种事件之一&#xff0c;当手指在组件上按下、滑动、抬起时触发。 名称是否冒泡功能描述onTouch(event: (event?: TouchEvent) > void)是手指触摸动作触发该回调&#xff0c;event返回值见下面TouchEvent介绍。 2. TouchEve…

开源VS闭源:大模型之争,究竟谁更胜一筹?

随着人工智能技术的快速发展&#xff0c;大模型作为其中的核心组件&#xff0c;已经引起了业界的广泛关注。在大模型的研发过程中&#xff0c;开源与闭源成为了两个备受争议的话题。究竟开源与闭源谁更好&#xff1f;本文将从多个角度进行深入分析&#xff0c;为大家揭示真相。…

【Python数据预处理系列】Pandas 数据操作实战:掌握 .loc[] 方法进行高效数据选取

文章将详细介绍.loc[]方法的各种使用场景&#xff0c;帮助读者深入理解并掌握这一核心功能。 在Pandas库中&#xff0c;.loc[]方法是一种强大而灵活的数据选取工具。本文将通过详细的步骤和示例&#xff0c;手把手教您如何利用这一工具进行高效的数据操作。 首先&#xff0c;我…

社区待就业人员信息管理系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;工作岗位管理&#xff0c;基础数据管理&#xff0c;预约面试管理&#xff0c;就业信息管理&#xff0c;公告信息管理 社区工作账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户…

【Visual Studio 2022 部署 .net core website】

部署网站 AdminPortal.csproj false Website File Nameappsettings.jsonAdminPortal.deps.jsonAdminPortal.runtimeconfig.json–web.configAPI.runtimeconfig.json

基于聚类与统计检验深度挖掘电商用户行为

1.项目背景 在当今竞争激烈的电商市场中,了解用户的行为和需求对于制定成功的市场策略至关重要,本项目通过建立RFM模型、K-Means聚类模型,将1000个用户进行划分,针对不同类的用户,提出不同的营销策略,最后通过统计检验来探究影响用户消费行为的因素和影响用户上网行为的…

GIt快速入门(一文学会使用Git)

GIt快速入门 文章目录 GIt快速入门一、为什么要学习Git二、Git的安装1.安装Git2.下载GUI 三、Git的概念1、版本控制2、集中式控制3、分布式控制4、多人协作开发1.并行开发2.分支管理3.冲突解决4.代码审查5.分布式特性 四、Git客户端操作1.界面介绍2.提交操作3.创建分支4.合并分…

【QT5】<总览三> QT常用控件

文章目录 前言 一、QWidget---界面 二、QPushButton---按钮 三、QRadioButton---单选按钮 四、QCheckBox---多选、三选按钮 五、margin&padding---边距控制 六、QHBoxLayout---水平布局 七、QVBoxLayout---垂直布局 八、QGridLayout---网格布局 九、QSplitter---…