算法通关村第一关—青铜挑战—用Java基本实现各种链表操作

news2025/1/24 7:22:57

文章目录

  • 第一关—链表【青铜挑战】
    • 1.1 单链表的概念
    • 1.2 链表的相关概念
    • 1.3 创建链表 - Java实现
    • 1.4 链表的增删改查
      • 1.4.1 遍历单链表 - 求单链表长度
      • 1.4.2 链表插入 - 三种位置插入
        • (1)在链表的表头插入
        • (2)在链表的中间插入
        • (3)在链表的结尾插入
        • (4)在链表的所有位置插入[总结]⭐
      • 1.4.3 链表删除 - 三种位置删除
        • (1)删除链表的表头结点
        • (2)删除链表的最后一个结点
        • (3)删除链表的中间结点
        • (4)删除链表的任一位置[总结]⭐

第一关—链表【青铜挑战】


1.1 单链表的概念

  • 单向链表就像一个铁链一样,元素之间互相连接,包含多个结点,每个结点**只有一个**指向后继元素的next指针,并且最后一个元素的next指向null

在这里插入图片描述

小练:以下两张图,是否满足单链表的要求

在这里插入图片描述
在这里插入图片描述
解析:

第一张图满足单链表的要求,第二张图不满足要求,因为c1它有两个后继结点a5和b4,单链表的核心是一个结点只能有一个后继,但是不代表一个结点只能有一个被指向(如c1可以被a2和b3指向)

注意:做题的时候注意比较的是值还是结点,有时可能两个结点的值是相等的,但并不是同一个结点,例如下图,有两个结点的值都是1,但并不是同一个结点

在这里插入图片描述


1.2 链表的相关概念

节点和头节点

在链表中,每个点都由指向下一个节点的地址组成独立的单元,成为一个结点,有时也称为节点,含义都是一样的

对于单链表而言,如果知道了第一个元素,就可以遍历访问整个链表,因此第一个节点最重要,一般称为头节点

虚拟结点

虚拟结点就是一个dummyNode,其next指针指向head头部,也就是dummyNode.next = head

因此,如果我们在算法里使用了虚拟结点,则要注意,如果要获得head结点,或者从方法里返回的时候,则应使用dummyNode.next

另外,dummyNode的val不会被使用,初始化为0或者-1等都是可以的,既然值不会被使用,那么我们就会有疑问?虚拟结点有啥用呢?简单来说,就是为了方便我们处理头结点,否则我们需要在代码里单独处理头结点【首部结点】的问题
在这里插入图片描述


1.3 创建链表 - Java实现

我们首先要理解JVM是怎么构建出链表,JVM里面有栈区和堆区,堆区主要存引用,也就是一个指向实际对象的地址,而堆区存的才是创建的对象

在这里插入图片描述

/**
 * @Author Zan
 * @Date 2023/11/29 14:46
 * @Description : 传入一个数组,将其转换成单链表
 */
public class BasicLink {

    public static void main(String[] args) {
        int[] a = {1, 2, 3, 4, 5, 6};
        Node head = initLinkedList(a);
        System.out.println(head);
    }

    private static Node initLinkedList(int[] array) {
        Node head = null, current = null;
        for (int i = 0; i < array.length; i++) {
            Node newNode = new Node(array[i]);
            if (i == 0) { // 头节点
                // 由于head = current,因此当current在变化的时候,head也在变化
                head = newNode;
//                newNode = new Node(array[i]); // 如果在此将newNode重新定义,指向的是不同的堆数据,因此head就只是一个Node普通对象,单节点的链表
                current = newNode;
            } else { // 后面的节点
                current.next = newNode;
                current = newNode;
            }
        }
        return head;
    }


    static class Node {
        public int x;
        public Node next;

        public Node(int x) {
            this.x = x;
            next = null;
        }
    }
}

在这里插入图片描述

我们可以看到初始化链表的时候,head和current指向的是同一个对象,也就是指向堆中的同一个数据,因此当控制current.next = newNode 的时候,其实就是控制堆中的数据指向谁,next指向下一条数据,而head跟current一样指向的是同一个对象,因此就可以跟随其变化

在这里插入图片描述
最后得到head如下图所示 - 单链表的形式

在这里插入图片描述

1.4 链表的增删改查

  • 对于单链表而言,不管进行什么操作,一定都是从头开始逐个向后开始访问,所以操作之后是否还能够找到表头非常重要

1.4.1 遍历单链表 - 求单链表长度

/**
 * 遍历链表,获取链表的长度
 * @param head 头节点
 * @return
 */
public static int getListLength(Node head) { // 传入头节点
    int length = 0;
    Node node = head;
    while (node != null) { // 一个一个节点遍历
        length++;
        node = node.next;
    }
    return length;
}

1.4.2 链表插入 - 三种位置插入

  • 单链表的插入,和数组的插入一样,过程不复杂。但是单链表的插入操作需要考虑三种情况:首部、中部和尾部

(1)在链表的表头插入
  1. 创建新结点newNode
  2. 新结点的next = head,即newNode.next = head
  3. 头head指向新的链表,即head = newNode

在这里插入图片描述

/**
 * 在链表的表头插入
 * @param head 原链表
 * @param nodeInsert 要插入表头的结点元素
 * @return
 */
public static Node insertNodeByHead(Node head, Node nodeInsert) {
    nodeInsert.next = head;
    head = nodeInsert;
    return head;
}

在这里插入图片描述


(2)在链表的中间插入
  1. 循环找到要插入位置position的前一个结点(位置从1开始)
  2. 将插入结点的next指向前一个结点的next,即nodeInsert.next = newNode.next
  3. 将前一个结点的next指向插入结点,即newNode.next = nodeInsert
  • 注意:我们不能先将前一个结点的next指向插入结点,这是因为每个结点都只有一个next,因此如果先将前一个结点的next指向插入结点,那么15->7这一条线就断掉了,也就导致后面的7、40将会找不到,断开

在这里插入图片描述

/**
 * 在链表的中间位置插入
 * @param head 原链表的头结点
 * @param nodeInsert 要插入的结点
 * @param position 要插入的位置,从1开始
 * @return
 */
public static Node insertNodeByPosition(Node head, Node nodeInsert, int position) {
    Node newNode = head; // 不对原链表进行操作,用新链表指向堆中的同一个元素,进行堆中的操作
    int i = 1;
    while (i < position - 1) { // 要在中间位置插入,因此要获取插入位置的前一个结点,这样子才能将next连接起来
        newNode = newNode.next;
        i++;
    }
    nodeInsert.next = newNode.next; // 将要插入的结点的next指向插入位置前一个结点的next
    newNode.next = nodeInsert; // 将插入位置前一个结点的next指向要插入的结点
    return head;
}

在这里插入图片描述


(3)在链表的结尾插入
  1. 获取原链表总共有多少个元素
  2. 循环遍历找到最后一个结点
  3. 将最后一个结点的next指向新结点

在这里插入图片描述

/**
 * 在链表的结尾插入
 * @param head 原链表的头结点
 * @param nodeInsert 要插入的结点
 * @return
 */
public static Node insertByEnd(Node head, Node nodeInsert) {
    Node newNode = head;
    int nodeLength = getListLength(newNode); // 获取到原链表的元素个数
    int i = 1;
    while (i < nodeLength) { // 循环遍历找到最后一个结点
        newNode = newNode.next;
        i++;
    }
    newNode.next = nodeInsert; // 将最后一个结点的next指向新结点
    return head;
}

在这里插入图片描述


(4)在链表的所有位置插入[总结]⭐
/**
 * 链表的插入(所有情况,表头、中间、结尾)
 *
 * @param head 原链表
 * @param nodeInsert 插入的结点
 * @param position 插入的位置,从1开始
 * @return
 */
public static Node insertNode(Node head, Node nodeInsert, int position) {
    // head原链表中没有数据,插入的结点就是链表的头结点
    if (head == null) {
        return nodeInsert;
    }

    // 获取存放元素个数 - 进行校验(position在[1, size]之间)
    int size = getListLength(head);
    if (position > size + 1 || position < 1) {
        System.out.println("位置参数越界");
        return head;
    }

    // 表头插入
    if (position == 1) {
        nodeInsert.next = head;
        head = nodeInsert;
        return head;
    }

    // 中间插入和结尾插入
    Node newNode = head;
    int count = 1;
    while (count < position - 1) {
        count++;
        newNode = newNode.next;
    }
    nodeInsert.next = newNode.next;
    newNode.next = nodeInsert;

    return head;
}

1.4.3 链表删除 - 三种位置删除

  • 删除同样分为删除头部元素、删除中间元素和删除尾部元素

(1)删除链表的表头结点

将head表头向前移动一次之后,原先的结点就变成了不可达,会被JVM回收掉

在这里插入图片描述

/**
 * 删除表头结点
 * @param head 原链表
 * @return
 */
public static Node deleteByHead(Node head) {
    head = head.next;
    return head;
}

在这里插入图片描述


(2)删除链表的最后一个结点
  1. 获取该链表的总长度size
  2. 找到倒数第二个结点
  3. 将倒数第二个结点的next指向null,即newNode.next = null

在这里插入图片描述

/**
 * 删除最后一个结点
 * @param head 原链表
 * @return
 */
public static Node deleteByEnd(Node head) {
    Node newNode = head;
    int size = getListLength(head); // 获取该链表的总长度size
    int i = 1;
    while (i < size - 1) { // 找到倒数第二个结点
        i++;
        newNode = newNode.next;
    }
    newNode.next = null; // 将倒数第二个结点的next指向null
    return head;
}

在这里插入图片描述


(3)删除链表的中间结点
  1. 找到要删除结点的前一个结点
  2. 将前一个结点的next指向下下个结点,即newNode.next = newNode.next.next

在这里插入图片描述

/**
 * 删除中间结点
 * @param head 原链表
 * @return
 */
public static Node deleteByPosition(Node head, int position) {
    Node newNode = head;
    int i = 1;
    while (i < position - 1) {
        i++;
        newNode = newNode.next;
    }
    newNode.next = newNode.next.next;
    return head;
}

在这里插入图片描述


(4)删除链表的任一位置[总结]⭐
/**
 * 删除结点(三种情况,表头、中间、最后一位结点)
 * @param head 原链表
 * @return
 */
public static Node deleteNode(Node head, int position) {
    // 如果没有结点,说明无法删除,直接返回null即可
    if (head == null) {
        return null;
    }

    //校验
    int size = getListLength(head);
    if (position > size || position < 1) { // 这里不是size+1,而插入是size+1,因为插入可以插入到最后一位(未知的最后一位),而删除必须要是已知的,不能是未知的越界
        System.out.println("输入的参数有误");
        return head;
    }

    if (position == 1) { // 删除头节点
        return head.next;
    } else { // 删除中间结点或者最后一个结点
        Node newNode = head;
        int count = 1;
        while (count < position - 1) {
            count++;
            newNode = newNode.next;
        }
        newNode.next = newNode.next.next;
        return head;
    }
}

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

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

相关文章

sqlserver调试clr程序集

右键项目属性&#xff0c;选择调试&#xff0c;勾选sqlserver调试选项&#xff1a; 选择附加到进程调试&#xff0c;勾选显示所有用户的进程&#xff0c;选择sqlserver.exe进程&#xff0c;打好程序集的断点&#xff0c;基于sqlserver触发clr程序集&#xff0c;即可中止到相关断…

个人开发者是否能借助开源获利?

前言 近几年国内外开源软件的成倍增长&#xff0c;以及开源软件在互联网领域的比重越来越高&#xff0c;开源软件扮演者越来越重要的角色&#xff0c;所以开源软件不仅推动了技术领域的不断创新和发展&#xff0c;也给个人开发者们提供了越来越多的合作机会和获利的机会。同时&…

二叉树(判断是否为单值二叉树)

题目&#xff08;力扣&#xff09;&#xff1a; 判断二叉树上每个节点的值是否相同&#xff0c;就需要让root节点分别与左节点和右节点分别比较是否相同。 注意&#xff1a;root等于空时&#xff0c;直接可以返回true&#xff1b; 首先&#xff0c;先判断他的特殊情况&#x…

序列号管理

序列号管理&#xff0c;将从以下方面进行学习和阐述 WHY 为什么需要序列号&#xff0c;有什么作用 HOW sap如何进行管理序列号 WHEN 什么情况下适合进行序列号管理 1、 什么是序列号 首先简单介绍一个序列号是什么东西&#xff0c;我们使用的手机、电脑或者大家…

HarmonyOS4.0开发应用(一)【工具安装】

工具安装 地址:https://developer.harmonyos.com/cn/develop/deveco-studio#download 我是windows&#xff0c;所以安装的windows 解压后双击该文件进行安装 安装完成后可选择是否导入开发工具的设置 这里选择不导入进入到工具内 点击Next进行安装 都没问题最终就可以开…

操作系统原理-作业一-进程同步

1.某理发店可同时供 10 人理发&#xff0c;当店中顾客少于 10 人时&#xff0c;则店外的顾客可立即进入&#xff0c;否则需在外面等待。请定义所需信号量并写出信号量各种取值( 大于 0 、等于 0 、小于0)分别代表的含义&#xff0c;并用 P 、 V 操作编程实现完成多个顾…

最高可挽回 20% 损失!东京大学利用 AI 及无人机,预测农作物最佳采收日期

内容一览&#xff1a;如果能在短期内确定并预测田间所有作物的生长状况&#xff0c;就可以设定最佳采收日期&#xff0c;减少非标准尺寸作物的数量&#xff0c;并最大限度地减少收入损失。对此&#xff0c;来自东京大学和千叶大学的研究人员&#xff0c;给出了 AI 无人机解决方…

华为鸿蒙:安卓,拜拜了您呢!

9 月底&#xff0c;华为举办了今年的秋季全场景新品发布会&#xff0c;接近尾声的时候&#xff0c;华为终端 BG CEO 余承东突然宣布&#xff0c;鸿蒙 HarmonyOS NEXT 即将发布&#xff0c;鸿蒙原生应用全面启动。 不同于之前 HarmonyOS 基于 AOSP&#xff08;Android 开放源代…

Ebullient 硬件篇

一. 简介 哈喽&#xff0c;大家好&#xff0c;好久没有给大家分享新项目了&#xff0c;但之前分享了许多项目都没有认认真真的做完过&#xff0c;做到了一半&#xff0c;由于某些原因就放弃了&#xff0c;给自己的一种感觉是做了很多东西&#xff0c;但是能拿出来讲的缺没有几…

Verilator 用法

Verilating … 威尔逊-斯奈德版权所有 2003-2023。 … SPDX 许可证标识符&#xff1a; 仅限 LGPL-3.0 或 Artistic-2.0 验证 Verilator 可通过五种主要方式使用&#xff1a; 使用 --cc 或 :vlopt:-sc 选项&#xff0c;Verilator 将分别把设计翻译成 C 或 SystemC 代码。 将设计…

Redis 入门和环境搭建

认识Redis Redis是一种NoSQL数据库&#xff0c;以键值对形式存储数据&#xff0c;支持多种数据结构&#xff0c;包括字符串、哈希、列表、集合、有序集合等&#xff0c;使其适用于多种应用场景。由于所有数据都存储在内存中&#xff0c;Redis的读写性能非常高。同时&#xff0…

Xiamen I Fitness Platform

厦门I健身平台程 https://ijs.sports.xm.gov.cn/mgh5/#/ 1&#xff09;公众号 2&#xff09;主页 3&#xff09;【个人中心】【我的保险】就是要买一份保险&#xff0c;10元的那种&#xff0c;不然去场地出意外咋办 4&#xff09;我的保险状态&#xff1a;未购买&#xff0c;…

Web实现悬浮球-可点击拖拽禁止区域

这次要实现的是这种效果&#xff0c;能够在页面上推拽和点击的&#xff0c;拖拽的话&#xff0c;就跟随鼠标移动&#xff0c;点击的话&#xff0c;就触发新的行为&#xff0c;当然也有指定某些区域不能拖拽&#xff0c;接下来就一起来看看有什么难点吧~ 需要监听的鼠标事件 既…

30岁+项目经理和PMO少奋斗10年的职业规划路线

大家好&#xff0c;我是老原。 很多项目经理小白出来工作遇到困惑时又以得过且过的态度拒绝了别人的指导和建议&#xff0c;磨磨蹭蹭的就到了30岁。 大多数人会感到迷茫的原因&#xff0c;是因为对自己要往什么方向发展&#xff1f;做什么样的事情毫无计划和想象。 为什么会…

goweb入门教程

本文是作者自己学习goweb时写的笔记&#xff0c;分享给大家&#xff0c;希望能有些帮助 前言&#xff1a; 关于web&#xff1a;本质 ​ ​ web中最重要的就是浏览器和服务器的request(请求)和response(响应)&#xff1b; ​ 一个请求对应一个响应。 一个请求对应一个响应&…

从独立求存到登顶市场,荣耀为何能在手机红海翻出新的浪花?

对企业的价值评估&#xff0c;往往离不开对其所处行业前景的考量。在蓝海赛道布局的企业&#xff0c;往往要比在红海市场突围的企业更容易受到资本重视。 但这并非绝对&#xff0c;若是一家企业能够在饱和的红海市场中&#xff0c;实现新的增长&#xff0c;其蕴涵的成长价值便…

【LeetCode刷题】-- 78.子集

78.子集 class Solution {public List<List<Integer>> subsets(int[] nums) {List<List<Integer>> ans new ArrayList<>();List<Integer> list new ArrayList<>();dfs(0,nums,ans,list);return ans;}private void dfs(int cur,int…

maven 常用命令解析

maven 是什么 Maven 是一个流行的项目管理和构建工具&#xff0c;用于帮助开发人员管理 Java 项目的构建、依赖管理和文档生成等任务。它提供了一种标准化的项目结构和一套规范来管理项目的生命周期。 Maven 的主要功能包括&#xff1a; 项目对象模型&#xff08;Project Obje…

【AI数字人-论文】Wav2lip论文解读

文章目录 Wav2lip前言Lip-sync Expert DiscriminatorGeneratorvisual quality discriminator生成器总损失函数 论文 Wav2lip 前言 Wav2Lip 是第一个通用说话者的模型&#xff0c;可生成与真实同步视频相匹配的口型同步精度的视频&#xff0c;它的核心架构概括为“通过向训练有…

服务器之间的conda环境迁移

有的时候python环境中可能包含了我们编译好的很多库文件&#xff0c;如果在别的服务器想直接使用环境就会比较困难些。最好的办法就是直接迁移环境。而传统的迁移方法导出“*.yaml”环境配置的这种方法&#xff0c;实际是需要重新安装环境&#xff0c;对于这种安装好的环境是不…