[Data structure]单链表常见算法题

news2024/9/29 9:32:44

⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章
⭐作者主页:@逐梦苍穹
⭐所属专栏:数据结构。数据结构专栏主要是在讲解原理的基础上拿Java实现
⭐如果觉得文章写的不错,欢迎点个关注一键三连😉有写的不好的地方也欢迎指正,一同进步😁

目录

  • 1、有效节点的个数
  • 2、查找单链表中的倒数第k个结点
    • 2.1、方式一
    • 2.2、方式二
  • 3、反转单链表
  • 4、从尾到头打印单链表
    • 4.1、方式一
    • 4.2、方式二
    • 4.3、⭐方式三
  • 5、合并有序链表
    • 5.1、方式一
    • 5.2、方式二
  • 6、所有代码

单链表知识请查看另外一篇文章,其中有详细的介绍,已经链表类和节点类的实现:
[Data structure]单链表 | 一文介绍线性数据结构之一的单链表(Java实现)

五道单链表常见算法题:

  1. 求单链表中有效节点的个数
  2. 查找单链表中的倒数第k个结点[新浪面试题]
  3. 单链表的反转[腾讯面试题]
  4. 从尾到头打印单链表,不破坏链表结构[百度面试题]
  5. 合并两个有序的单链表,合并之后的链表依然有序

五道题解法总览如下:
在这里插入图片描述

1、有效节点的个数

  思路:传入的头节点为空节点,头节点的下一个节点才是链表的内容。如果一开始判断头节点没有下一个节点,则什么为空链表,直接返回0;不为空则进入while循环,每次指针后移一位,total就自增一次,直到指针下一个节点为空跳出循环。
在这里插入图片描述

2、查找单链表中的倒数第k个结点

2.1、方式一

  按链表长度查找:传入参数为链表的头节点和需要的是倒数的第几个节点。
  思路分析:链表除去头节点之外,其他存在的节点个数之和即为链表的有效节点个数(也可以说是链表的长度)。链表倒数第K个节点的位置为:(链表长度 - k的值 + 1)。想要获取到节点的地址,则需要遍历到目标节点的上一个节点,即需要遍历到的位置是:链表长度 - k的值,也就是length - index。
在这里插入图片描述

2.2、方式二

  双指针遍历:先创建两个节点,初始地址均指向头节点的下一个节点(第一个有效节点)。让temp1指针向前先移动n位(n的值取决与k的值是多少,即取决于传入的index整型参数的值)。此时让temp1和temp2两个指针同时开始移动,每次移动一个节点,当前驱指针的下一个节点为null的时候,说明前驱指针temp1已经到达链表的尾部,那么此时后继指针temp2指向的就是"倒数第k个节点 的 前一个节点"。
在这里插入图片描述

3、反转单链表

此题的思路为:使用三个辅助节点(前驱节点prev、当前节点current、后继节点next),完成对链表的反转。
步骤如下:

  1. 先将前驱节点prev置为空,当前节点为第一个有效节点,后继节点为第二个有效节点
  2. 把当前节点的下一个节点地址,设置为prev,即为null;然后把后继节点next的下一个节点地址设置为当前节点(此时就完成了第二个有效数据排在了第一个有效数据之前的操作)
  3. 把前驱节点、当前节点、后继节点指针均往后移动一位,然后继续重复之前的操作
  4. 直到当前节点为null,说明节点遍历完成。
  5. 此时的前驱节点prev指向了原链表最后一个节点(即反转后的链表的第一个有效节点),此时只需要把原链表的头节点指向当前的prev,即完成了链表的反转
    在这里插入图片描述
    在这里插入图片描述

4、从尾到头打印单链表

三种实现方式:
在这里插入图片描述

4.1、方式一

获取有效个数的长度,从头到尾打印:这个算法没啥好说的,只能说 能跑。但是性能很低。
思路:获取有效个数长度,把节点依次存入数组,对数组反向遍历。
在这里插入图片描述

4.2、方式二

采用递归的方式,在每一次打印之前,都先调用自身,所以最后打印的顺序,就是从后往前
在这里插入图片描述

4.3、⭐方式三

  栈是一种常见的数据结构,它具有“先进后出”(Last-In-First-Out,LIFO)的特点。栈是由一系列节点组成的集合,其中每个节点包含一个元素和一个指向下一个节点的指针。
  栈的基本操作包括:入栈(push)、出栈(pop)、查看栈顶元素(peek)和判断栈是否为空(isEmpty)等。其中,入栈操作会在栈的顶部添加一个新的元素,出栈操作会从栈的顶部删除一个元素,并返回被删除的元素。查看栈顶元素操作则返回栈顶的元素,而判断栈是否为空则返回一个布尔值,表示栈中是否有元素。
  这里采用栈的方式,先得到的对象,压入栈中,则后得到的对象,先弹出栈,所以就实现了逆序打印。
在这里插入图片描述

5、合并有序链表

两种方式实现:
在这里插入图片描述

先介绍第一种低性能的方式(仅限于 能跑),然后再介绍第二种双指针的方式。

5.1、方式一

  这种方法性能很低,比较原始,采用的是直接把两个有序链表的内容,存入数组,之后采用冒泡排序对数组内的链表节点对象进行排序。之后再通过遍历的方式,把每个节点的next域重新赋值。
在这里插入图片描述

5.2、方式二

双指针合并的实现步骤如下:

  1. 创建一个新的链表,用来存放合并后的结果
  2. 定义两个指针,分别指向两个链表的头结点
  3. 比较两个指针所指节点的值的大小,将较小的节点添加到新链表中,并将指针向后移动一位
  4. 重复上述步骤,直到有一个链表为空
  5. 将另一个链表中剩余的节点直接添加到新链表的末尾
  6. 返回新链表的头结点

在这里插入图片描述
在这里插入图片描述

6、所有代码

package linkedList.singleList;

import java.util.Stack;

/**
 * @author 逐梦苍穹
 * @date 2023/5/1 8:57
 */
public class AlgorithmQuestion {
    /**
     * 求单链表中有效节点的个数
     */
    public static int getLength(Node headNode) {
        int total = 0;
        if (headNode.getNext() == null) return total;
        while (headNode.getNext() != null) {
            total++;
            headNode = headNode.getNext();
        }
        return total;
    }

    /**
     * 查找单链表中的倒数第K个节点
     */
    //方式一:按链表长度寻找
    public static Node findLastIndexNodeMethod1(Node head, int index) {
        int temp = 0;
        int length = getLength(head);
        int nodeFontIndex = length - index;
        while (true) {
            if (temp == nodeFontIndex) {
                break;
            }
            head = head.getNext();
            temp++;
        }
        return head.getNext();
    }

    //方式二:双指针遍历
    public static Node findLastIndexNodeMethod2(Node head, int index){
        Node temp1 = head.getNext();
        Node temp2 = head.getNext();
        if (head.getNext() == null) return head;
        for (int i = 0; i < index; i++) {
            temp1 = temp1.getNext();
            if (i == index - 1 && temp1 == null) return temp2;
            if (temp1 == null){
                System.out.println("链表长度不足");
                return head;
            }
        }
        while (temp1.getNext() != null){
            temp1 = temp1.getNext();
            temp2 = temp2.getNext();
        }
        return temp2.getNext();
    }

    /**
     * 反转单链表
     */
    public static void reverseLinkedList(Node headNode) {
        if (headNode == null || headNode.getNext() == null) return;
        Node prev = null;
        Node current = headNode.getNext();
        while (current != null) {
            Node next = current.getNext();
            current.setNext(prev);
            prev = current;
            current = next;
        }
        headNode.setNext(prev);
    }

    /**
     * 从尾到头打印链表(不破坏链表结构)
     */
    //方式一:获取有效个数的长度,从尾到头打印
    public static void reversePrintMethod1(Node headNode) {
        int length = getLength(headNode);
        Node[] nodeArr = new Node[length];
        if (length == 0) return;
        if (length == 1) {
            System.out.println(headNode.getNext());
            return;
        }
        for (int i = 0; i < length; i++) {
            nodeArr[i] = headNode.getNext();
            headNode = headNode.getNext();
        }
        for (int j = length - 1; j >= 0; j--) {
            System.out.println(nodeArr[j]);
        }
    }

    //方式二:递归
    public static void reversePrintMethod2(Node headNode) {
        if (headNode == null || headNode.getNext() == null) return;
        reversePrintMethod2(headNode.getNext());
        System.out.println(headNode.getNext());
    }

    //方式三:栈
    public static void reversePrintMethod3(Node headNode) {
        if (headNode.getNext() == null) {
            System.out.println("链表为空");
            return;
        }
        Stack<Node> stack = new Stack<>();
        Node temp = headNode.getNext();
        while (temp != null) {
            stack.push(temp);
            temp = temp.getNext();
        }
        while (!stack.empty()) {
            System.out.println(stack.pop());
        }
    }

    /**
     * 合并两个有序的单链表,合并之后的链表依然有序
     */
    //方式一:存入数组再合并
    public static SingleList mergeTwoSingleListMethod1(SingleList list1,SingleList list2){
        SingleList singleList = new SingleList();
        Node singleListHead = singleList.getHead();

        Node list1HeadNode = list1.getHead();
        int list1Length = getLength(list1.getHead());
        Node list2HeadNode = list2.getHead();
        int list2Length = getLength(list2.getHead());
        Node[] mergeArray = new Node[list1Length + list2Length];
        for (int i = 0; i < list1Length; i++) {
            mergeArray[i] = list1HeadNode.getNext();
            list1HeadNode = list1HeadNode.getNext();
        }
        for (int j = list1Length; j < list1Length + list2Length; j++) {
            mergeArray[j] = list2HeadNode.getNext();
            list2HeadNode = list2HeadNode.getNext();
        }
        for (int k = 0; k < mergeArray.length - 1; k++) {
            for (int l = 0; l < mergeArray.length - 1 - k; l++) {
                if (mergeArray[l].getId() > mergeArray[l + 1].getId()){
                    Node temp = mergeArray[l];
                    mergeArray[l] = mergeArray[l + 1];
                    mergeArray[l + 1] = temp;
                }
            }
        }

        for (int n = 0; n < mergeArray.length; n++) {
            if (n != mergeArray.length - 1) {
                singleListHead.setNext(mergeArray[n]);
                singleListHead = singleListHead.getNext();
            }
        }
        return singleList;
    }

    //方式二:双指针合并
    public static Node mergeTwoSingleListMethod2(Node head1, Node head2) {
        if (head1 == null) {
            return head2;
        }
        if (head2 == null) {
            return head1;
        }

        Node temp = new Node(0,0);
        Node cur = temp;
        head1 = head1.getNext();
        head2 = head2.getNext();
        while (head1 != null && head2 != null) {
            if (head1.id <= head2.id) {
                cur.setNext(head1);
                head1 = head1.getNext();
            } else {
                cur.setNext(head2);
                head2 = head2.getNext();
            }
            cur = cur.getNext();
        }

        cur.setNext ((head1 == null) ? head2 : head1);

        return temp;
    }
}

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

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

相关文章

Hibernate(二)——Springboot整合Hibernate

在了解了Hibernate后&#xff0c;进一步将Hibernate和Springboot整合。 目录 引入依赖配置文件代码BeanDao层Service层Controller层 测试JpaRepository接口 引入依赖 <!--引入hibernate--> <dependency><groupId>org.springframework.boot</groupId>…

【大数据处理与可视化】四、数据预处理

【大数据处理与可视化】四、数据预处理 实验目的实验内容实验步骤一、案例——预处理部分地区信息1、导包2、读取文件3、检查并删除重复数据北京天津&#xff08;无重复数据&#xff09; 4、检查缺失值北京&#xff08;无缺失值&#xff09;天津&#xff08;向前填充&#xff0…

10分钟学会搭建sovits第一篇

So-vits-svc 基于端到端架构的VITS和soft-vc&#xff0c;用户只需准备几十分钟到几个小时不等的语音或歌声数据&#xff0c;就能制作&#xff08;训练&#xff09;属于自己的 AI 声库 &#xff08;前提是你的显卡足够给力&#xff09;&#xff0c;将一段语音或歌声转换为你想要…

简易时钟-QT学习

1 .h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPaintEvent> //绘制事件 #include <QPainter> //画家类 #include <QPaintDevice> #include <cmath> // #include <QPainterPath> #include <QTime> //时间类…

基于蜣螂算法的无人机航迹规划-附代码

基于蜣螂算法的无人机航迹规划 文章目录 基于蜣螂算法的无人机航迹规划1.蜣螂搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用蜣螂算法来优化无人机航迹规划。 1.蜣螂搜索算法 …

【LeetCode】646. 最长数对链

646. 最长数对链&#xff08;中等&#xff09; 思路 这道题和 300. 最长递增子序列 类似&#xff0c;我们可以定义 dp 数组&#xff0c;其中 dp[i] 表示以 i 结尾的子序列的性质。在处理好每个位置后&#xff0c;统计一遍各个位置的结果即可得到题目要求的结果。 但是题目中强…

ElasticSearch(二)简介

1. 简介 Elasticsearch 是一个分布式、高扩展、高实时的搜索与数据分析引擎。 它能很方便的使大量数据具有搜索、分析和探索的能力。充分利用Elasticsearch的水平伸缩性&#xff0c;能使数据在生产环境变得更有价值。 Elasticsearch 的实现原理主要分为以下几个步骤&#xf…

Servlet执行原理和API详解

一、HttpServlet 我们写 Servlet 代码的时候, 首先第⼀步就是先创建类, 继承⾃ HttpServlet, 并重写其中的某些方法. 1.1核心方法 1.2处理GET或POST请求 1.3数据的两种提交方式 数据提交有两种方式&#xff1a; form 表单提交ajax 提交 1.3.1form 表单提交 form表单提交的…

瑞萨e2studio(24)----电容触摸配置(1)

瑞萨e2studio.20--电容触摸配置1 概述硬件准备新建工程工程模板保存工程路径芯片配置工程模板选择时钟配置添加TOUCH驱动配置CapTouch开启调优界面启动 CapTouch 调优通过电容触摸点亮LED 概述 这篇文档将创建一个使用 e2 studio 集成 QE 的电容式触摸应用示例&#xff0c;通…

C语言函数大全-- s 开头的函数(3)

C语言函数大全 本篇介绍C语言函数大全-- s 开头的函数&#xff08;3&#xff09; 1. sleep 1.1 函数说明 函数声明函数功能unsigned int sleep(unsigned int seconds);它是 C 语言标准库中的函数&#xff0c;用于使当前进程挂起一定的时间。在挂起期间&#xff0c;操作系统…

移动宽带安装说明一(刘欣)

2023年&#xff0c;五一假期给老家和父母家安装了2次宽带&#xff0c;记录一下吧。 一、移动光改覆盖率已经很高了 从当初的铁通“FTTB”覆盖小区,网线入户的带宽只能达到100M&#xff0c;提升到现在大面积的光改完成&#xff0c;普遍是光猫&#xff08;光纤MODEL&#xff09…

网络协议与攻击模拟-04-实施ARP攻击与欺骗

实施 ARP 欺骗和攻击 一、环境 1、 kali Linux 安装一个 arpspoof 2、win10 被攻击主机 二、 kaili 配置 kali Linux 系统是基于 debian Linux 系统&#xff0c;采用 deb 包管理方式&#xff0c;可以使用 apt 的方式进行直接从源安装 1、配置源 # 官方源 # deb http:/…

FastAPI如何区分多环境:开发/测试/预发布/生产环境

1 缘起 开始用FastAPI开发项目&#xff0c; 区分环境是部署的第一步&#xff0c;因此&#xff0c;需要区分dev/test/pre/prod等环境&#xff0c; 而FastAPI刚好提供了读取环境配置文件的参数&#xff0c;可以在启动服务时指定环境配置文件的路径&#xff0c; 参数为env_file&a…

【前端技术】Vue3 01:初识 Vue.js

Vue 可以说是非常流行了&#xff0c;至少在国内是这样&#xff0c;他是个轻量级的 JavaScript 框架&#xff0c;非常适合构建大型和中小型的 Web 应用程序&#xff0c;如果想和前端打交道&#xff0c;应该绕不过这个框架吧。 目录 1 Vue.js 介绍 2 IDE 选择 2.1 vscode 2.…

libfacedetection 人脸检测库的基本使用

目录 1、源码下载 2、编译 3、构建工程 4、个人总结 运行总结&#xff1a; 与CascadeClassifier级联分类器 人脸检测 对比: 1、源码下载 直接从github上克隆项目仓库。 git clone https://github.com/ShiqiYu/libfacedetection.git2、编译 这个项目使用了cmake脚本&#…

【LeetCode】413. 等差数列划分

413. 等差数列划分&#xff08;中等&#xff09; 思路 由于题目求的是等差数列 &#xff0c;很自然想到子数组一定满足 nums[i] - nums[i-1] nums[i-1] -nums[i-2]; 。然而我们对于 dp 数组的定义通常是以 i 结尾&#xff0c;满足某些条件的子数组数量&#xff0c;而等差数组可…

【LeetCode】300. 最长递增子序列

300. 最长递增子序列&#xff08;中等&#xff09; 方法一&#xff1a;动态规划 思路 通常来说&#xff0c;子序列不要求连续&#xff0c;而子数组或子字符串必须连续&#xff1b;对于子序列问题&#xff0c;第一种动态规划方法是&#xff0c;定义 dp 数组&#xff0c;其中 dp[…

PHP语言基础

&#x1f331;博客主页&#xff1a;大寄一场. &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 目录 前言 一、变量和数据类型 二、运算符和表达式 三、条件语句 四、循环语句 前言 PHP是一种非常流行的开源服务器端脚本语言&#xff0c;广泛用于Web开发…

Python sys模块

sys:使用或维护解释器 版本信息 Python版本 >>> sys.version 3.10.3 (tags/v3.10.3:a342a49, Mar 16 2022, 13:07:40) [MSC v.1929 64 bit (AMD64)]系统版本 >>> sys.platform win32 >>> 解释器版本 >>> sys.hexversion 50988016 >&g…

TypeScript 基本概念

TypeScript 是什么&#xff1f; 目标&#xff1a;能够说出什么是 TypeScript TS 官方文档 TS 中文参考 - 不再维护 TypeScript 简称&#xff1a;TS&#xff0c;是 JavaScript 的超集&#xff0c;JS 有的 TS 都有 TypeScript Type JavaScript&#xff08;在 JS 基础之上…