【数据结构】单链表面试题(Java + 力扣 + 详解)

news2024/11/22 22:18:41

🎇🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!
人生格言: 当你的才华撑不起你的野心的时候,你就应该静下心来学习!
欢迎志同道合的朋友一起加油喔 💪💪💪
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个🐒嘿嘿
谢谢你这么帅气美丽还给我点赞!比个心

链表面试题

  • 一、移除链表元素
    • 1. 题目
    • 2. 解析
    • 3. 完整代码展示
  • 二、反转链表
    • 1. 题目
    • 2. 解析
    • 3. 完整代码展示
  • 三、链表的中间结点
    • 1. 题目
    • 2. 解析
    • 3. 完整代码展示
  • 四、 链表中倒数第k个节点
    • 1. 题目
    • 2. 解析
    • 3. 完整代码展示
  • 五、合并两个有序链表
    • 1. 题目
    • 2. 解析
    • 3. 完整代码展示

一、移除链表元素

203.移除链表元素

1. 题目


在这里插入图片描述


2. 解析


  • 特殊情况处理:
  • 如果链表头节点 head 为空,直接返回 null,表示链表为空,无需处理。
if(head == null){
    return null;
}

在这里插入图片描述

  • 一般情况处理:
  • 定义两个指针,prevNode 指向当前节点前一个节点(初始为头节点)cur 指向当前节点下一个节点
ListNode prevNode = head;
ListNode cur = head.next;
  • 循环遍历链表:
  • 使用 cur 指针遍历链表,当 cur 不为 null 时执行循环。
  • 如果 cur 指向的节点的值等于 val,则将 prevNode.next 指向 cur.next,即跳过当前节点 cur
  • 否则,更新 prevNode 为 cur,并将 cur 移动到下一个节点。
while(cur != null){
    if(cur.val == val){
        prevNode.next = cur.next;
        cur = cur.next;
    }else{
        prevNode = cur;
        cur = cur.next;
    }
}
  • 处理头节点:
  • 最后处理头节点,如果头节点的值等于 val,则将头节点 head 指向下一个节点,即跳过头节点。
if(head.val == val){
    head = head.next;
}
  • 返回链表头:
  • return head; 返回处理后的链表头节点。

3. 完整代码展示


/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        //特殊情况、链表为空
        if(head == null){
            return null;
        }

        //处理一般情况
        //定义两个指针,prev 指向第一个节点,cur 指向第二个节点
        ListNode prevNode = head;
        ListNode cur = head.next;

        //循环判断条件 cur != null
        //先不考虑 头节点
        //从第二个节点开始排查
        while(cur != null){
            if(cur.val == val){
                prevNode.next = cur.next;
                cur = cur.next;
            }else{
                prevNode = cur;
                cur = cur.next;
            }
        }
        //最后处理头节点
        if(head.val == val){
            head = head.next;
        }
        return head;
    }
}

二、反转链表

206.反转链表


1. 题目


在这里插入图片描述


2. 解析

  • 要求:遍历链表一遍就反转完
  • 思路:
  • 先把原链表的头节点的next置为空,记录好下一个节点,进行头插

在这里插入图片描述


  • reverseList 方法用于反转单链表,接收一个头节点 head 作为参数,并返回反转后的新头节点。

  • 首先进行了几个边界条件的判断:

     如果 head 为 null,即空链表,直接返回 null。
     如果链表只有一个节点 (head.next == null),则直接返回 head,因为反转后还是自身。
    
  • 若链表节点数大于1,进入反转过程:

    ListNode cur = head.next; 初始化 cur 为 head 的下一个节点。
    head.next = null; 将 head 的 next 指针置为 null,表示反转后的尾节点。
    
  • 使用头插法反转链表:

    while (cur != null) 循环直到 cur 为 null:
    ListNode curNext = cur.next; 记录当前节点 cur 的下一个节点。
    

    cur.next = head; 将 cur 的 next 指向 head,完成头插操作。
    head = cur; 更新 head 为当前 cur,即新的头节点。
    cur = curNext; 将 cur 移动到下一个节点,继续反转操作。

  • 最终返回 head,此时 head 已经是反转后链表的新头节点。

  • 这种方法的时间复杂度为 O(n),其中 n 是链表的节点数,因为需要遍历整个链表一次来完成反转操作。


3. 完整代码展示

/**
 * Definition for singly-linked list.
 * public class ListNode {
 * int val;
 * ListNode next;
 * ListNode() {}
 * ListNode(int val) { this.val = val; }
 * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        // 如果链表为空
        if (head == null) {
            return null;
        }
        // 如果只有一个节点
        if (head.next == null) {
            return head;
        }
        ListNode cur = head.next;
        head.next = null;

        // 进行头插
        // 进行头插之前要记录好下一个节点
        while (cur != null) {
            ListNode curNext = cur.next;
            cur.next = head;
            head = cur;
            cur = curNext;
        }
        return head;
    }
}

三、链表的中间结点

876.链表的中间结点


1. 题目


在这里插入图片描述


2. 解析


  • 第一种解法:长度 / 2

但是这种 需要遍历一遍链表,还要再除以2,可以在这个基础上进行优化

  • 第二种解法:

定义两个指针:(快慢指针)fast快指针 slow慢指针


在这里插入图片描述


算法采用了快慢指针技巧:

  • fast 指针每次移动两步:fast = fast.next.next;
  • slow 指针每次移动一步:slow = slow.next;

由于 fast 指针移动速度是 slow 指针的两倍,所以当 fast 指针到达链表末尾时,slow 指针恰好指向链表的中间节点。

具体执行过程如下:

  • 初始时,fastslow 都指向链表的头节点 head

  • 在每一轮循环中,首先检查 fast 是否为空且 fast.next 是否为空(即 fast 是否到达链表尾部)

    如果是,则退出循环,此时 slow 指向链表的中间节点。
    如果不是,则将 fast 向前移动两步,将 slow 向前移动一步。
    
  • 循环结束后,返回 slow,即为链表的中间节点。

  • 这段代码的时间复杂度为 O(n),其中 n 是链表的长度,因为 fast 指针最多遍历链表一次。空间复杂度为 O(1),因为只使用了常量级的额外空间用于两个指针。


3. 完整代码展示


/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    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;
    }
}

四、 链表中倒数第k个节点

链表中倒数第k个节点

1. 题目


在这里插入图片描述


2. 解析


  • 定义了一个简单的链表节点类 ListNode,每个节点包含一个整数值 val 和一个指向下一个节点的引用 next

  • indKthToTail(ListNode head, int k) 方法接收一个链表头节点 head 和一个整数 k,表示要查找倒数第k个节点。
  • 首先,使用两个指针 fast 和 slow,初始化都指向头节点 head
  • fast 先移动 k 步,如果 k 大于链表长度,则直接返回 null
  • 然后,fastslow 同时向前移动,直到 fast 到达链表末尾,此时 slow 指向的即为倒数第 k 个节点。
  • 返回 slow。

  • 在 main 方法中,创建了一个包含五个节点的链表。
  • 分别测试了找倒数第1个、第3个节点以及超过链表长度的情况。

3. 完整代码展示

// 定义链表节点类
class ListNode {
    int val;
    ListNode next;
    ListNode(int x) {
        val = x;
    }
}

public class Solution {
    public ListNode FindKthToTail(ListNode head, int k) {
        if (head == null || k <= 0) {
            return null;
        }

        ListNode fast = head;
        ListNode slow = head;

        // 快指针先向前移动k步
        for (int i = 0; i < k; i++) {
            if (fast != null) {
                fast = fast.next;
            } else {
                // 如果k超过了链表长度,直接返回null
                return null;
            }
        }

        // 快慢指针一起向前移动,直到快指针到达链表末尾
        while (fast != null) {
            fast = fast.next;
            slow = slow.next;
        }

        // 此时slow指向的就是倒数第k个节点
        return slow;
    }

    // 测试代码
    public static void main(String[] args) {
        Solution solution = new Solution();

        // 创建链表:1 -> 2 -> 3 -> 4 -> 5
        ListNode head = new ListNode(1);
        head.next = new ListNode(2);
        head.next.next = new ListNode(3);
        head.next.next.next = new ListNode(4);
        head.next.next.next.next = new ListNode(5);

        // 测试找倒数第1个节点
        ListNode result = solution.FindKthToTail(head, 1);
        if (result != null) {
            System.out.println(result.val); // 输出5
        } else {
            System.out.println("找不到节点");
        }

        // 测试找倒数第3个节点
        result = solution.FindKthToTail(head, 3);
        if (result != null) {
            System.out.println(result.val); // 输出3
        } else {
            System.out.println("找不到节点");
        }

        // 测试k超过链表长度
        result = solution.FindKthToTail(head, 6);
        if (result != null) {
            System.out.println(result.val);
        } else {
            System.out.println("找不到节点"); // 输出找不到节点
        }
    }
}

五、合并两个有序链表

21.合并两个有序链表


1. 题目

在这里插入图片描述


2. 解析


  • 虚拟节点的创建:
ListNode newNode = new ListNode(1);

这里创建了一个值为 1 的虚拟节点 newNode。这个虚拟节点的作用是作为合并后链表的头节点的前一个节点,简化了链表操作。

  • 临时节点:
ListNode tmp = newNode;

tmp 节点用来迭代构建合并后的链表,最初指向 newNode,随着节点的合并不断移动。

  • 合并过程:

使用 while 循环,只要 list1 和 list2 都不为 null,比较它们当前节点的值:
如果 list1.val < list2.val,将 tmp.next 指向 list1,然后 list1 向后移动一位,tmp 也向后移动一位。
否则,将 tmp.next 指向 list2,然后 list2 向后移动一位,tmp 也向后移动一位。

  • 处理剩余节点:

循环结束后,可能会有一个链表还有剩余节点未合并,使用两个 if 条件分别处理:
如果 list1 不为 null,将剩余的 list1 接在 tmp.next 后面。
如果 list2 不为 null,将剩余的 list2 接在 tmp.next 后面。
返回合并后链表的头节点:返回 newNode.next,即虚拟节点 newNode 的下一个节点,即合并后的链表的头节点。

  • 总结

这段代码通过使用虚拟节点和临时节点,在一次遍历中将两个有序链表 list1 和 list2 合并为一个有序链表,并返回合并后链表的头节点。

3. 完整代码展示

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        //创建一个虚拟节点
        ListNode newNode = new ListNode(1);
        //临时节点
        ListNode tmp = newNode;
        while(list1 != null && list2 != null){
            if(list1.val < list2.val){
                tmp.next = list1;
                list1 = list1.next;
                tmp = tmp.next;
            }else{
                tmp.next = list2;
                list2 = list2.next;
                tmp = tmp.next;
            }
        }
        if(list1 == null){
            tmp.next = list2;
        }
        if(list2 == null){
            tmp.next = list1;
        }
        return newNode.next;
    }
}

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

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

相关文章

【日常记录】【插件】Typed.js:用于创建打字效果的 JavaScript 库

文章目录 1. 引言2. 安装3. 基本使用参考链接 1. 引言 Typed.js是一个用于创建打字效果的 JavaScript 库。这个效果就是 chatgpt、百度的文心一言等其他的大模型&#xff0c;回复用户的问题的时候的效果 typed-js 官网typed 案例 2. 安装 CDN方式 这俩都可以&#xff0c;还有其…

17 推荐系统方案中那些不得不知的坑

你好&#xff0c;我是大壮。《易经》中说&#xff1a;“上九&#xff1a;亢龙有悔”。上九是指阳爻在卦中处于最高位&#xff0c;亢龙是指飞向尽头的龙&#xff0c;穷尽至极力终有尽时&#xff0c;力尽则悔&#xff0c;悔不可及。 在前面的 18 讲我们已经讨论了整个推荐算法的…

C语言中的IO控制流

文章目录 一、什么是C语言中的IO控制流二、open函数 1.使用open函数创建文件2.使用使用open函数打开文件三、文件的权限四、文件的描述符五、read函数六、write函数七、lseek函数八、close函数 一、什么是C语言中的IO控制流 在linux系统中一切皆文件&#xff0c;C语言中的IO控…

【SQL 新手教程 4/20】关系模型 --索引

&#x1f497; 关系数据库建立在关系模型上⭐ 关系模型本质上就是若干个存储数据的二维表 记录 (Record)&#xff1a; 表的每一行称为记录&#xff08;Record&#xff09;&#xff0c;记录是一个逻辑意义上的数据 字段 (Column)&#xff1a;表的每一列称为字段&#xff08;Colu…

Java | Leetcode Java题解之第299题猜数字游戏

题目&#xff1a; 题解&#xff1a; class Solution {public String getHint(String secret, String guess) {int bulls 0;int[] cntS new int[10];int[] cntG new int[10];for (int i 0; i < secret.length(); i) {if (secret.charAt(i) guess.charAt(i)) {bulls;} e…

C语言条件语句中switch语句的一些用法

目录 1. switch语句写一个简单的switch语句 2. if语句和else语句的对比判断3的倍数情况 3. switch语句中break详解4. switch语句的一项小练习5. switch语句中的default6. switch语句中的顺序问题 1. switch语句 C语言除了if...else...作为条件语句外&#xff0c;还提供了switc…

Java(十)——接口

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 ⚡开源项目&#xff1a; rich-vue3 &#xff08;基于 Vue3 TS Pinia Element Plus Spring全家桶 MySQL&#xff09; &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1…

数据库表的行列转换(行转列,列转行)

目录 前言 行转列 创建测试表 score1 插入测试数据 需求与通用SQL写法 列转行 创建测试表 score2 插入测试数据 需求与通用SQL写法 前言 在工作中&#xff0c;多多少少都会遇到一些数据展示的需求&#xff0c;开发一个接口&#xff0c;从数据库中查询数据返回页面展示…

Thinkphp仿华为商城源码/红色风格电脑手机数码商城系统网站源码

Thinkphp仿华为商城&#xff0c;主要实现了商品首页展示、用户意见、商品分类列表、商品搜索、商品详细展示、购物车、订单生成、在线付款、以及个人中心完善个人资料、用户修改收货地址、余额查询、消费查询、订单管理、商品评价、热销商品和最近商品浏览&#xff1b; 后台是…

Langchain核心模块与实战[8]:RAG检索增强生成[loader机制、文本切割方法、长文本信息处理技巧]

Langchain核心模块与实战[8]:RAG(Retrieval Augmented Generation,检索增强生成) RAG(Retrieval-Augmented Generation)技术是一种结合检索和生成功能的自然语言处理(NLP)技术。该技术通过从大型外部数据库中检索与输入问题相关的信息,来辅助生成模型回答问题。其核心…

面试重点---快速排序

快排单趟 快速排序是我们面试中的重点&#xff0c;这个知识点也很抽象&#xff0c;需要我们很好的掌握&#xff0c;而且快速排序的代码也是非常重要&#xff0c;需要我们懂了还不行&#xff0c;必须要手撕代码&#xff0c;学的透彻。 在研究快速排序之前&#xff0c;我们首先…

使用 Arduino 实现 PID 控制器

使用 Arduino 实现 PID 控制器 PID controller implementation using Arduino (microcontrollerslab.com) In this article, you will learn how to design PID controller using Arduino. PID controller can implemented using both analog and digital electronics. But in…

Java 基础 and 进阶面试知识点(超详细)

一个 Java 文件中是否可以存在多个类&#xff08;修饰类除外&#xff09;&#xff1f; 一个 Java 文件中是可以存在多个类的&#xff0c;但是一个 Java 文件中只能存在一个 public 所修饰的类&#xff0c;而且这个 Java 文件的文件名还必须和 public 所修饰类的类名保持一致&a…

斯坦福UE4 C++课学习补充 14:UMG-优化血量条

文章目录 一、优化执行效率二、简单脉冲动画 一、优化执行效率 绑定事件需要每一帧检查绑定对象是否有变化&#xff0c;势必造成CPU资源的浪费&#xff0c;因此优化执行效率的思路是&#xff1a;UI组件不再自行每帧查询血量&#xff0c;而是让血量自己在发生变化的同时通知UI进…

软件全过程建设资料,交付资料,方案等

在软件开发过程中&#xff0c;文档扮演着至关重要的角色。它不仅记录了项目的需求、设计和开发过程&#xff0c;还为项目的维护和管理提供了便利。本文将详细介绍软件开发文档的重要性和作用&#xff0c;以及需求分析、软件设计、开发过程、运维管理和项目管理等方面的文档编写…

【机器学习】深入理解损失函数(Loss Functions)

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 深入理解损失函数(Loss Functions)什么是损失函数?常见损失函数类型1. 均方误差…

[译] 深入浅出Rust基金会

本篇是对 RustConf 2023中的Rust Foundation: Demystified这一视频的翻译与整理, 过程中为符合中文惯用表达有适当删改, 版权归原作者所有. 大家好,我是Sage Griffin,我的代词是they/them。我今天来这里是要谈谈Rust基金会。 要了解基金会实际做什么,我们需要理解美国国内税收…

微软第四季度财报预览:增长动力追踪

微软公司即将在2024年7月30日&#xff08;周二&#xff09;美国市场收盘后发布第四季度财务结果。 微软的收益 - 预期如何 美股券商开户通道 市场预计&#xff0c;微软即将到来的2024年第四季度的收入将年增长14.5%&#xff0c;达到644亿美元&#xff0c;高于2023年第四季度…

【Leetcode】二十、记忆化搜索:零钱兑换

文章目录 1、记忆化搜索2、leetcode509&#xff1a;斐波那契数列3、leetcode322&#xff1a;零钱兑换 1、记忆化搜索 也叫备忘录&#xff0c;即把已经计算过的结果存下来&#xff0c;下次再遇到&#xff0c;就直接取&#xff0c;不用重新计算。目的是以减少重复计算。 以前面提…

【.NET 6 实战--孢子记账--从单体到微服务】--开发环境设置

在这一小节&#xff0c;我们将设置开发环境。 一、安装SDK 咱们的项目使用的是 .NET6&#xff0c;开发前我们需要从官网上下载.NET6 SDK&#xff08;点击下载&#xff09;&#xff0c;这里要注意的是我们需要下载.NET6 SDK&#xff0c;而不是 .NET6 Runtiem 。SDK 包含 Runti…