数据结构(Java实现):链表习题

news2024/9/22 3:43:13

文章目录

  • 1. 题目列表及链接
  • 2. 题目解析及代码
    • 2.1 删除链表中等于给定值 val 的所有节点
    • 2.2 反转一个单链表
    • 2.3 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点
    • 2.4 输入一个链表,输出该链表中倒数第k个结点
    • 2.5 将两个有序链表合并为一个新的有序链表并返回,新链表是通过拼接给定的两个链表的所有节点组成的
    • 2.6 以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前
    • 2.7 链表的回文结构
    • 2.8 输入两个链表,找出它们的第一个公共结点
    • 2.9 给定一个链表,判断链表中是否有环
    • 2.10 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL

1. 题目列表及链接

  1. 删除链表中等于给定值 val 的所有节点。
  2. 反转一个单链表。
  3. 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
  4. 输入一个链表,输出该链表中倒数第k个结点。
  5. .将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
  6. 编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前 。
  7. 链表的回文结构。
  8. 输入两个链表,找出它们的第一个公共结点。
  9. 给定一个链表,判断链表中是否有环。
  10. 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL 。

2. 题目解析及代码

2.1 删除链表中等于给定值 val 的所有节点

题目解析
这道题需要移除所有的目标节点,我们可以使用两个辅助节点 prev(前驱节点),cur(遍历节点)。prev在cur前面。开始使用cur往前遍历,当cur指向的节点不是目标节点,prev跟随cur一起往后移动一步,当cur指向的节点是目标节点,使cur继续往后遍历,prev不再往后走,并使prev的next为cur遍历后的位置。
在这里插入图片描述
代码示例

class Solution {
    public ListNode removeElements(ListNode head, int val) {
    //先判断传入指针是否为空
        if(head==null){
            return head;
        }
        //上面如果不判断,下面这行可能会有空指针异常
        ListNode cur=head.next;
        ListNode prev=head;
        //进入循环开始遍历
        while (cur!=null){
        //如果不是目标节点,prev和cur一起往后移动
            if (cur.val != val) {
                prev = cur;
                cur=cur.next;//这一步可以合起来,这里方便理解
            }else{//否则只移动cur,并把prev的next指向cur
                prev.next=cur.next;
                cur=cur.next;
            }
        }
        //最后判断头节点是否为目标节点
        //这一步也可以在最前面来写,但是要以循环的方式,防止头节点一直为目标节点
        if(head.val==val){
            head=head.next;
        }
        return head;
    }
}

2.2 反转一个单链表

题目解析
要反转一个链表,我们采用头插法,然后一步一步往后遍历节点,将遍历到的节点(cur)的next指向头结点,并且让当前节点设置成头结点。此事我们就需要考虑,cur的next指向改变了,怎么才能让cur正常遍历完原来的链表,这时我们使用curN来指向原链表cur的next。
在这里插入图片描述

代码示例

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null){//判断传入头节点是否为空
            return null;
        }
        //如果不加上面判断,下面这行可能引发空指针异常
        ListNode cur=head.next;  
        //使尾节点的next置null
        head.next=null;
        //遍历初始链表,并反转链表
        while(cur!=null){
            ListNode curN=cur.next;//防止初始链表的顺序丢失
            //头插
            cur.next=head;
            head=cur;
            //使cur的顺序回到初始链表的顺序
            cur=curN;
        }
        return head;
    }
}

2.3 给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点

题目解析
解法1:首先想到的是把链表的节点个数求出来,然后遍历节点个数/2次就能得到中间节点。
解法2:使用快慢指针,快指针一次走两个节点,慢指针一次走一个节点。根据经典数学问题:追击问题可以知道,快指针走的路程是慢指针的两倍,所以当快指针遍历完链表后,慢指针就刚好到链表的中间节点。

代码示例

//解法1
class Solution {
    public ListNode middleNode(ListNode head) {
        int count = size(head);
        ListNode cur = head;
        for(int i=0;i<count/2;i++,cur=cur.next){}//遍历节点个数/2次就能得到中间节点
        return cur;
    }
    public int size(ListNode head){//计算链表长度
        int count=0;
        ListNode cur=head;
        while(cur!=null){
            cur=cur.next;
            count++;
        }
        return count;
    }
}
//解法2
class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode fast = head;//快指针
        ListNode slow = head;//慢指针
        //这里判断条件顺序不能改变
        //否则当fast为null时,fast.next可能会有空指针异常
        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
        }
        return slow;
    }
}

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

题目解析
给的题目保证了k的位置是合法的,但是为了严谨我们首先要判断传入的节点是否合法。(有关链表的题目都要注意传入的形参的合法性)。这题我们同样可以使用快慢指针的思想,先让快指针移动k-1个节点。然后快慢指针一起往后移动,当快指针遍历完链表,慢指针就指向链表的倒数第k个节点。
代码示例

class Solution {
    public int kthToLast(ListNode head, int k) {
        if(k<=0||head==null){//判断传入参数是否合法
            return -1;
        }
        ListNode fast=head;//快指针
        ListNode slow=head;//慢指针
        //快指针先走k-1
        for(int i=0;i<k-1;i++){
            fast=fast.next;
            if(fast==null){//链表到头了还没走完k-1个节点,k的位置就不合法
                return -1;
            }
        }
        //快慢指针一起往后移动
        while(fast.next!=null){//快指针遍历完结束
            fast=fast.next;
            slow=slow.next;
        }
        return slow.val;//返回慢指针的值
    }
}

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

题目解析
这里我们创建一个虚拟头结点,然后分别遍历两个链表,当我们去遍历每个节点的时候开始比较两个节点的值。小的那个值尾插到新头的链表。最后,如果某条链表遍历完成,另一条链表的剩余部分直接尾插。
在这里插入图片描述
代码示例

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
    if(list1==null){//如果list1为null,返回list2
        return list2;
    }else if(list2==null){//如果list2为null,返回list1
        return list1;
    }
    ListNode list = new ListNode();//虚拟头结点
    ListNode cur = list;//辅助尾插
    while(true){
        if(list1.val<list2.val){//比较值
            cur.next=list1;//尾插
            list1=list1.next;//list1往后遍历
        }else{
            cur.next=list2;//尾插
            list2=list2.next;//list2遍历
        }
        cur=cur.next;//cur往后走以便下一次尾插
        //下面当某一条链表为null时,另一条链表的剩余部分直接尾插
        if(list1==null){
            cur.next=list2;
            break;
        }else if(list2==null){
            cur.next=list1;
            break;
        }
    }
    //最后虚拟头结点的next即为合并的链表
    return list.next;
    }
}

2.6 以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前

题目解析
我们可以将一个链表分为两条链表,一条小于x的节点,一条大于或等于x的节点。然后分别记录两条链表的头结点和尾节点。然后将第一条链表的尾结点接入第二条链表的头结点。此时有一个特殊情况:传入链表的节点都小于x或者传入链表的节点都大于等于x。而且要注意,第二条链表的尾的next可能不指向空。
代码示例

public class Partition {
    public ListNode partition(ListNode pHead, int x) {
        if(pHead==null){//判断传入链表是否为null
            return null;
        }
        ListNode bs=null;//第一条链表的头
        ListNode be=null;//第一条链表的尾(遍历)
        ListNode as=null;//第二条链表的头
        ListNode ae=null;//第二条链表的尾(遍历)

        ListNode cur=pHead;//遍历当前链表
        while(cur!=null){
            if(cur.val<x){//小于x的节点插到第一条链表
                if(bs==null){//如果头为null,将节点设置为头节点
                    bs=be=cur;
                }else{//头节点部位null,尾插节点
                    be.next=cur;
                    be=be.next;
                }
            }else{//大于等于x的节点插到第二条链表
                if(as==null){//如果头为null,将节点设置为头节点
                    as=ae=cur;
                }else{//头节点部位null,尾插节点
                    ae.next=cur;
                    ae=ae.next;
                }
            }
            cur=cur.next;
        }
        //特殊情况
        //传入链表的节点都小于x或者传入链表的节点都大于等于x
        if(bs==null){
            return as;
        }
        be.next=as;
        //第二条链表的尾的next可能不指向空
        if(as!=null){
            ae.next=null;
        }
        return bs;
    }
}

2.7 链表的回文结构

题目解析
首先利用快慢指针找到链表的中间节点,然后从中间节点往右开始进行链表的翻转。最后从两头开始往中间遍历,一个一个节点比较,直到遍历到链表的中点。
在这里插入图片描述

代码示例

public class PalindromeList {
    public boolean chkPalindrome(ListNode A) {
    //判断传入的链表是否为null
        if(A==null){
            return true;
        }
        //快慢指针找到中间节点
        ListNode slow=A;
        ListNode fast=A;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        //开始翻转中间节点往右的链表
        ListNode cur=slow.next;
        while(cur!=null){
            ListNode curN=cur.next;
            cur.next=slow;
            slow=cur;
            cur=curN;
        }
        //从两头开始往中间遍历
        while(A!=slow){//当A==slow时,链表为奇数个节点
            if(slow.val!=A.val){//当遍历过程中有值不相等就不是回文链表
                return false;
            }
            if(A.next==slow){//当A.next==slow,链表为偶数个节点
                return true;
            }
            //遍历
            slow=slow.next;
            A=A.next;
        }
        //成功完成遍历,链表为回文链表
        return true;
    }
}

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

题目解析
在这里插入图片描述
tip:链表相交不可能只相交一个节点,因为节点的next只有一个。
首先我们求出两条链表的长度。然后用唱的那根链表先走差值个节点,然后两条链表一起往后走,直到相遇就说明两条链表相交且相遇节点就是首个公共节点。反之,当链表遍历完了还没有相遇,就不存在公共节点。
在这里插入图片描述
代码示例

public class Solution {
    public ListNode getIntersectionNode(ListNode head1, ListNode head2) {
    //如果其中一条链表为null,直接返回null
        if(head1==null||head2==null){
            return null;
        }
        //遍历两条链表求长度
        ListNode cur1=head1;
        ListNode cur2=head2;
        int count1=0;
        int count2=0;
        while(cur1!=null){
            count1++;
            cur1=cur1.next;
        }
        while(cur2!=null){
            count2++;
            cur2=cur2.next;
        }
        //使指针重新指向链表头
        cur1=head1;
        cur2=head2;
        //让长链表先走差值步
        if(count1>count2){
            for(int i=0;i<count1-count2;i++){
                cur1=cur1.next;
            }
        }else{
            for(int i=0;i<count2-count1;i++){
                cur2=cur2.next;
            }
        }
        //两条链表同时往前走
        while(cur1!=null){
            if(cur1==cur2){//当两指针相遇就到第一个公共节点了
                return cur1;
            }
            cur1=cur1.next;
            cur2=cur2.next;
        }
        //遍历完还没有相遇就不存在公共节点
        return null;
    }
}

2.9 给定一个链表,判断链表中是否有环

题目解析
这里我们还是使用快慢指针来解题,当链表存在环时,快慢指针一定会在环种相遇。没环,快指针先走到结尾。
思考
为什么快指针每次走两步,慢指针走一步可以?
假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚进环时,可能就和快指针相遇了,最差情况下两个指针之间的距离刚好就是环的长度。此时,两个指针每移动一次,之间的距离就缩小一步,不会出现每次刚好是套圈的情况,因此:在慢指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。
快指针一次走3步,走4步,…n步行吗?
在这里插入图片描述

代码示例

public class Solution {
    public boolean hasCycle(ListNode head) {
    //这理解不需要判断传入链表是否为null了,因为下面不会引发空指针异常
        ListNode fast=head;//快指针
        ListNode slow=head;//慢指针
        //循环条件顺序不能变,可能会造成空指针异常
        while(fast!=null&&fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow){//当相遇存在环
                return true;
            }
        }
        //结束循环没有相遇,不存在环
        return false;
    }
}

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

题目解析
首先我们使用快慢指针找到相遇节点。
在这里插入图片描述
在这里插入图片描述
当得到相遇节点后,使fast从新回到头,然后一起出发,再次相遇就是环的入口。
代码示例

public class Solution {
    public ListNode detectCycle(ListNode head) {
    	//快慢指针开始运动
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null){
            fast=fast.next.next;
            slow = slow.next;
            if(fast==slow){//相遇结束循环
                break;
            }
        }
        //当不是if条件使循环结束,这个链表无环
        if(fast==null||fast.next==null){
            return null;
        }
        //fast回到头
        fast=head;
        //一起运动直至相遇
        while(fast!=slow){
            fast=fast.next;
            slow=slow.next;
        }
        return fast;
    }
}

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

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

相关文章

Edge浏览器:Github加速插件,让你在国内自由自在的访问Github!

你是否有访问GitHub要么超级慢&#xff0c;要么无法访问的时刻&#xff0c;是不是感觉痛苦不已&#xff1b; 现在给大家分享我解决问题的方法。 点击浏览器右上角的三个点【…】按钮&#xff0c;然后选择【扩展】 选择【管理扩展】 点击【获取 Miscrosoft Edge扩展】 在搜索框…

解决方案:在jupyter notebook环境下安装不了numpy

文章目录 一、现象二、解决方案 一、现象 平台&#xff1a;autodl 镜像&#xff1a;PyTorch 2.0.0 Python 3.8(ubuntu20.04) Cuda 11.8 GPU&#xff1a;RTX 4090(24GB) * 1 CPU&#xff1a;12 vCPU Intel Xeon Platinum 8352V CPU 2.10GHz 内存&#xff1a;90GB 安装numpy环…

推荐两款好用的录屏软件

Ocam oCam 是一款功能强大的屏幕录制软件&#xff0c;主要用于录制电脑屏幕上的活动。它支持多种视频格式&#xff0c;包括 AVI、MP4、FLV、MOV、TS 和 VOB&#xff0c;同时也支持多种音频格式&#xff0c;如 MP3。oCam 可以录制视频并保存为各种视频格式&#xff0c;还可以编辑…

绘剪批量软件——绘剪批量软件

批量软件是一种可以批量处理大量数据或操作的软件。它通常通过自动化的方式&#xff0c;快速高效地完成任务&#xff0c;减少人工操作的时间和工作量。批量软件可以用于数据处理、文件转换、批量重命名、批量下载等各种场景。 绘剪批量软件——绘剪TK批量软件 AIWYZ77 批量软…

前端JS——补充内容

这期是番外篇&#xff0c;主要是补充一下&#xff0c;之前没有说完整的内容。 后面两期太仓促了&#xff0c;一些值得注意的细节没有提到 之前的内容可以点击&#xff1a; JS总结上 JS总结中 JS总结下——DOM操作 JS总结下——事件操作 前面的两篇总结没什么好补充的&…

[Algorithm][综合训练][mari和shiny][重排字符串]详细讲解

目录 1.mari和shiny1.题目链接2.算法原理详解 && 代码实现 2.重排字符串1.题目链接2.算法原理详解 && 代码实现 1.mari和shiny 1.题目链接 mari和shiny 2.算法原理详解 && 代码实现 自己的版本&#xff1a;三层循环暴力枚举 --> 超时 --> 40% …

[项目]-通讯录的实现

前言 各位师傅大家好&#xff0c;我是qmx_07&#xff0c;今天来结合前面所学知识点&#xff0c;写一个能够增删改查&#xff0c;持久化数据的通讯录功能 准备工作 项目 一般会写成多个文件来实现&#xff0c;调用&#xff0c;接口声明&#xff0c;接口实现&#xff0c;这是一…

游戏开发设计模式之组件模式

目录 组件模式在游戏开发中的具体应用案例是什么&#xff1f; 如何在Unity引擎中实现和优化组件模式&#xff1f; 组件模式与其他设计模式&#xff08;如观察者模式、状态模式&#xff09;在游戏开发中的比较优势在哪里&#xff1f; 组件模式 观察者模式 状态模式 综合比…

【python】OpenCV—Single Human Pose Estimation

文章目录 1、Human Pose Estimation2、模型介绍3、基于图片的单人人体关键点检测4、基于视频的单人人体关键点检测5、左右校正6、关键点平滑7、涉及到的库函数scipy.signal.savgol_filter 8、参考 1、Human Pose Estimation Human Pose Estimation&#xff0c;即人体姿态估计&…

通过主成分分析实现检测金融中的异常交易模式

主成分分析&#xff08;PCA&#xff09;是一种在机器学习和数据科学中广泛使用的降维技术。它的主要目的是将高维数据转换为低维数据&#xff0c;同时尽可能保留原始数据中的信息。以下是PCA的一些关键点&#xff1a; 1. 基本概念&#xff1a;PCA的核心思想是将n维特征映射到k维…

5步实现猫眼电影爬虫与k-means算法可视化分析

&#x1f34a;作者&#xff1a;计算机毕设匠心工作室 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目…

C#二叉搜索树算法

二叉搜索树算法实现原理 二叉搜索树&#xff08;Binary Search Tree&#xff0c;简称BST&#xff09;是一种节点有序排列的二叉树数据结构。它具有以下性质&#xff1a; 每个节点最多有两个子节点。 对于每个节点&#xff0c;其左子树的所有节点值都小于该节点值&#xff0c;…

MySQL数据库连接超时问题排查报告

1、问题描述 边端设备访问云端过程中有概率出现MySQL数据库连接超时报错&#xff0c;具体报错代码如下&#xff1a; [2024-08-13 13:47:44,036] ERROR in app: Exception on /est-tasks/start [POST] Traceback (most recent call last): File "/usr/local/lib/python3.1…

Java 入门指南:Map 接口

Map 接口是 Java 集合框架中的一个接口&#xff0c;它表示了一种键值对的映射关系。Map 接口提供了一种以键为索引的数据结构&#xff0c;通过键可以快速查找对应的值。在 Map 中&#xff0c;每个键只能对应一个值&#xff0c;键是唯一的&#xff0c;但值可以重复。 常用的实现…

在vscode上便捷运行php文件

目录 前言 1. 准备工作 2. 创建文件 3. 下载插件 4.设置访问配置文件 5. 配置默认浏览器 6. 进行验证 前言 对于学习安全的我们来说,部署环境,靶场,和配置环境都是习以为常的一件事情,平时访问靶场都是通过小皮来,今天突想着最近需要对一些漏洞的原理进行研究,所以需要能够…

ESP-WHO C++程序分析基础(七)

以按键部分的程序做为分析基础 先看app_button.hpp文件&#xff0c;文件的路径如下 examples/esp32-s3-eye/main/include/app_button.hpp // AppButton 类&#xff0c;继承自 Subject 类&#xff0c;表示应用程序按钮 首先是先定义了一个 appbutton的按键类&#xff0c;这个…

【计算机组成原理】汇总三、存储系统

三、存储系统&#xff08;存储器层次结构&#xff09; 文章目录 三、存储系统&#xff08;存储器层次结构&#xff09;1.存储器的分类1.1按在计算机中的作用&#xff08;层次&#xff09;❗多级存储结构&#xff08;层次化结构&#xff09;1.2按存储介质1.3按存取方式1.4按信息…

抢单源码修正版,带教程,自动抓取订单,十几种语言可自动切换

亚马逊抢单源码自动抓取订单任务邀请英文,西班牙语可自动切换语言亲测修正版。带完整开源的前后台。 西班牙,英文&#xff0c;巴西&#xff0c;中文&#xff0c;德国&#xff0c;拉法兰西&#xff0c;荷兰&#xff0c;缅甸&#xff0c;Sverige&#xff0c;日本&#xff0c;Trk…

C_02基础学习

c 语言 基础 gcc编译器 作用: 将代码文件编译为可执行文件 分类: 一步到位gcc 要编译的代码文件 -o 生成的可执行文件注意:要编译的代码文件可以是多个-o 生成的可执行文件:可以忽略不写,默认生成a.out文件 分步实现预编译:头文件展示,宏替换,选择型编译gcc -E 要编译的代码…

VMware NET Service在虚拟机关闭后仍然占用CPU - 解决方案

问题 VMware NET Service&#xff08;即vmnat.exe&#xff09;在虚拟机关闭后仍然占用CPU&#xff0c;这是VM 17.5.0 和 VM 17.5.1 软件本身存在的Bug&#xff0c;此问题已在 VM 17.5.2 版本修复&#xff0c;下文介绍解决方案。 时间&#xff1a;2024年8月 解决方案 临时方…