[算法通关村] 1.2 链表的插入

news2025/1/11 7:17:08

上一节我们谈到了链表的概念,以及链表的创建方法,忘记的小伙伴可以复习一下:

[算法通关村] 1.1 单向链表的创建


        今天我们来探究一下链表的插入,链表的插入共有 3 种可能性,分别是在链表的头部插入、在中间插入,和在链表的尾部插入(追加),不同的插入位置对应着不同的代码,所以需要一个一个来探究。 

在链表的中间插入

        我们先来看一个新节点 nodeInsert 如何插入到位置为 1 < k < n+1 的原节点前面。(n 为链表长度)

        不难想到,由于我们上一节中创建的是一个 “单向链表”,每一个节点仅知道下一个节点的位置,并不知道上一个节点的位置,就像 “猴子掰玉米” 一样。在插入过程中,为了不丢失节点,需要保证每一个节点都被引用(指向),才不会被 JVM 回收,我们可以看到如下的插入过程:

         首先需要将 nodeInsert.next 指向拟插入位置 k 的上一个节点的 next,即:

nodeInsert.next = current.next

        然后将 current.next 指向 nodeInsert,此时变量 current 指向节点的原 next 关系自动消失:(只能同时指向一个后继节点)

current.next = nodeInsert

在链表的头部插入

        在链表的头部插入需要移动头指针 head 。由于头插移动了头结点,内存地址发生变化,所以需要将返回的新节点赋与 linkedList,否则后续计算会出错。

        首先将待插入节点 nodeInsert 的 next 变量指向头结点,然后移动头指针指向 nodeInsert 即可:

        代码实现如下:

nodeInsert.next = linkedList;
linkedList = nodeInsert;
return linkedList;

//上面 return 还可以这么写:
//return nodeInsert;

在链表的尾部插入

        在链表的尾部插入主要有两步,首先需要一个指针(Node 型变量)指向尾结点,这就涉及到了链表的遍历,而在 “链表的中间插入” 环节,也会涉及到链表的遍历,我们可以将此代码复用:

// 移动指针到拟插入位置的前一个节点
int count = 1;
while (count < position - 1) {
    current = current.next;
    count++;
}

        然后只需要将尾结点 current.next 指向待插入的 nodeInsert 节点即可:

// 此代码可省略,实际上是赋予了 null 值
// 因无伤大雅,又与 “中间插入” 代码一致,故可复用
nodeInsert.next = current.next;

current.next = nodeInsert;

        插入过程的图形化展示如下:

注意事项及完整代码

         上述原理很容易理解,但在实际操作中,还需要判断插入位置是否越界,即:

  • 最少在 position = 1 前插入(头部插入)
  • 至多在 position = getLength(linkedList) + 1 前插入(尾部插入)

        当插入位置 position 的值不满足上述要求时,我们会手动抛出一个异常:

// 短路或 先判断简单的
if (position < 1 || position > (getLength(linkedList) + 1)) {
            throw new Exception("参数:拟插入位置 越界");
        }

        其次,我们还需要查看待操作链表是否为一个空链表,这在方法 getLength 中就会进行判断,所以无需重复写判空语句:

public static int getLength(NewNode linkedList) throws Exception {
    int length = 0;
    NewNode current = linkedList;
    while (current != null) {
        length++;
        current = current.next;
    }
    if (length == 0) {
        throw new Exception("链表不存在");
    }
    return length;
}

        除此之外,还实现了 toString 方法用于遍历链表查看效果,该方法不是必须的。最后给出完整代码:

class NewNode {

    public int val;
    public NewNode next;

    NewNode(int val) {
        this.val = val;
        this.next = null;
    }
}

public class BasicLinkList {

    /**
     * 链表插入
     *
     * @param linkedList 链表头节点
     * @param nodeInsert 待插入节点
     * @param position   在该位置前插入
     * @return 插入后得到的新链表(指向头节点)
     */
    public static NewNode insertNode(NewNode linkedList, NewNode nodeInsert, int position) throws Exception {
        // 越界判断:
        //   最少在 position = 1 前插入(头插),
        //   最多在 position = getLength(linkedList) + 1 前插入(尾插)
        // 短路或 先判断简单的
        if (position < 1 || position > (getLength(linkedList) + 1)) {
            throw new Exception("参数:拟插入位置 越界");
        }

        //在链表开头插入
        if (position == 1) {
            nodeInsert.next = linkedList;
//            return nodeInsert;
            //上面return还可以这么写:
            linkedList = nodeInsert;
            return linkedList;
        }

        NewNode current = linkedList;
        int count = 1;
        // 移动指针到拟插入位置的前一个节点
        while (count < position - 1) {
            current = current.next;
            count++;
        }
        nodeInsert.next = current.next;
        current.next = nodeInsert;

        return linkedList;
    }

     /**
     * 获取链表长度
     *
     * @param linkedList 链表头节点
     * @return 链表长度
     */
    public static int getLength(NewNode linkedList) throws Exception {
        int length = 0;
        NewNode current = linkedList;
        while (current != null) {
            length++;
            current = current.next;
        }
        if (length == 0) {
            throw new Exception("链表不存在");
        }
        return length;
    }

    /**
     * 输出链表
     *
     * @param head 头节点
     */
    public static String toString(NewNode head) {
        NewNode current = head;
        StringBuilder stringBuilder = new StringBuilder();
        while (current != null) {
            stringBuilder.append(current.val).append("\t");
            current = current.next;
        }
        return stringBuilder.toString();
    }

    public static void main(String[] args) throws Exception {

        // 先创建一个链表
        NewNode linkedList = BasicLink.initLinkedList(new int[]{1, 2, 3, 4, 5});

        NewNode newNode;

        // 中间插入节点:在第 3 个节点前,插入 val = 30
        newNode = new NewNode(30);
        System.out.println(BasicLinkList.toString(BasicLinkList.insertNode(linkedList, newNode, 3)));
        System.out.println("========");
        // 头部插入节点:插入 val = 72
        newNode = new NewNode(72);
        // 由于头插移动了头结点,内存地址发生变化,所以需要将返回的新节点赋与 linkedList,否则后续计算会出错
        linkedList = BasicLinkList.insertNode(linkedList, newNode, 1);
        System.out.println(BasicLinkList.toString(linkedList));
        System.out.println("========");
        // 尾部插入节点:插入 val = 9
        newNode = new NewNode(9);
        System.out.println(BasicLinkList.toString(BasicLinkList.insertNode(linkedList, newNode, BasicLinkList.getLength(linkedList)+1)));
    }
}

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

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

相关文章

CMake学习-All in one

参考引用 CMake与Make最简单直接的区别通过例子学习CMakeLIO-SAM 1. 引言 1.1 什么是 CMake 1.1.1 CMake 背景 1.1.2 CMake 定义 CMake 是一个跨平台的安装&#xff08;编译&#xff09;工具&#xff0c;可以用简单的语句来描述所有平台的安装&#xff08;编译&#xff09;过…

基础算法-数组模拟队列

队列&#xff1a;先进先出 什么叫做队列&#xff1a; 就是一个特殊的数组。这个数组&#xff0c;最前面叫队头&#xff0c;最后面叫队尾。只允许在最后面添加元素&#xff0c;只允许在最前面删除元素。 解题思路&#xff1a; 用一个数组 q 保存数据。 用 hh 代表队头&…

关于PyTorch中一维卷积Conv1d的理解

首先明确一点&#xff0c;PyTorch中的一维卷积是从左往右做的&#xff0c;不是从上往下。 然后明确第二点&#xff0c;一维卷积和二维卷积最大的区别在于&#xff0c;一维卷积的卷积方向只有一个维度&#xff0c;一维卷积的卷积核不像二维卷积核一样可以左右和上下两个维度移动…

Mysql-主从复制与读写分离

Mysql 主从复制、读写分离 一、前言&#xff1a;二、主从复制原理1.MySQL的复制类型2. MySQL主从复制的工作过程;3.MySQL主从复制延迟4. MySQL 有几种同步方式&#xff1a;5.Mysql应用场景 三、主从复制实验1.主从服务器时间同步1.1 master服务器配置1.2 两台SLAVE服务器配置 2…

CRM系统化整合从N-1做减法实践 | 京东物流技术团队

1 背景 京销易系统已经接入大网、KA以及云仓三个条线商机&#xff0c;每个条线商机规则差异比较大&#xff0c;当前现状是独立实现三套系统分别做支撑。 2 目标 2022年下半年CRM目标是完成9个新条线业务接入&#xff0c;完成销售过程线上化&#xff0c;实现销售规则统一。 …

【Git】—— 分⽀的基本操作

目录 &#xff08;一&#xff09;理解分⽀ &#xff08;二&#xff09;创建分⽀ &#xff08;三&#xff09;切换分⽀ &#xff08;四&#xff09;合并分⽀ &#xff08;五&#xff09;删除分⽀ 总结 &#xff08;一&#xff09;理解分⽀ 本章开始介绍 Git 的杀⼿级功能之…

结构型设计模式:装饰器模式

设计模式专栏目录 创建型设计模式-单例模式/工厂模式/抽象工厂 行为型设计模式&#xff1a;模板设计模式/观察者设计模式/策略设计模式 结构型设计模式&#xff1a;装饰器模式 C#反射机制实现开闭原则的简单工厂模式 目录 设计模式专栏目录设计模式分类设计模式的设计原则装饰…

Java体系总览

一、基础篇 JVM JVM内存结构 堆、栈、方法区、直接内存、堆和栈区别 Java内存模型 内存可见性、重排序、顺序一致性、volatile、锁、final 垃圾回收 内存分配策略、垃圾收集器&#xff08;G1&#xff09;、GC算法、GC参数、对象存活的判定 JVM参数及调优 Java对象模型 …

windows环境下docker数据迁移到其他盘

docker安装在C盘&#xff0c;使用一段时间后&#xff0c;C盘爆满。因此想把C盘中的数据迁移到其他盘&#xff0c;以释放C盘空间。分为以下步骤&#xff1a; 1、启动docker软件&#xff0c;打开PowerShell并切换到Docker Compose配置文件的目录。 Docker Compose配置文件的目录…

zabbix监控docker容器

1、安装zabbix-agent2插件 需要被监控的主机安装zabbix-agent2插件&#xff0c;请参考另一篇博客进行安装。原有的zabbix-agent插件不支持docker容器的监控的。agent的功能&#xff0c;agent2也都有 http://t.csdn.cn/dccqw 并在被监控的主机中开放10050端口 firewall-cmd --z…

【雕爷学编程】Arduino动手做(171)---micro:bit 开发板2

37款传感器与模块的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止37种的。鉴于本人手头积累了一些传感器和模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&#xff0c;这…

用于事实核查的知识图谱比较推理:问题定义和算法 7.24

用于事实核查的知识图谱比较推理&#xff1a;问题定义和算法 摘要介绍问题定义知识段&#xff08;Knowledge Segment KS&#xff09;共性不一致性集体共性集体不一致性成对比较推理集体比较推理 知识片段提取Predictate-Predictate Similarity特定边的知识段特定子图知识段 比较…

[巅峰极客2023]wp复现

文章目录 [巅峰极客2023]复现miscwelcomefoundmesong学生物 webunserializesql [巅峰极客2023]复现 misc welcome base64解码 foundme find.DMP文件 使用flag查找工具找到关键字&#xff1a; flag.avif 放入010中看到这个hint&#xff0c;找到好几个avif&#xff1a; 将他…

Shedskin 使用

Shedskin是一个编译器工具&#xff0c;可以将Python代码编译为C语言。先说结论吧&#xff0c;这玩意现在就只是个玩具&#xff0c;因为使用ShedSkin编译的程序不能自由使用Python标准库&#xff0c;目前只支持大约17个常用模块&#xff1a; bisect collections ConfigParser c…

4.python设计模式【建造者模式】

内容: 将一个复杂对象的构建与它的表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。建造者模式与抽象工厂模式相似&#xff0c;也用来创建复杂对象。主要区分是建造者模式着重一步步构造复杂对象&#xff0c;而抽象工厂模式着重于多个系列的产品对象。角色&#xf…

嵌入式做单片机的门槛

我个人认为并不高&#xff0c;如果你非要有个量化的标准&#xff0c;那我觉得初中文凭都能学会并且能以此为生的程度。 文凭嘛&#xff0c;就是一张纸&#xff0c;并代表不了什么。 前几年&#xff0c;我接了一个帮研究生写毕业论文的单子&#xff0c;果然没让我失望&#xf…

实现Android屏幕分享和视频聊天(附源码)

在一些有人际互动的手机APP中&#xff0c;增加语音视频聊天功能是一个常见的需求。而现在&#xff0c;更进一步&#xff0c;在某些场景下&#xff0c;我们需要能将自己的手机屏幕分享给他人&#xff0c;或者是观看他人的手机屏幕。那么&#xff0c;这些常见的功能是如何实现的了…

reset master

1 reset master 执行 reset master; 后 变化1 &#xff1a;位点被重置 变化2 binlog日志被清空 原来的binlog被删除 从 mysql-bin.000001 开始记录。

【ribbon】Ribbon的负载均衡和扩展功能

Ribbon的核心接口 参考&#xff1a;org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration IClientConfig&#xff1a;Ribbon的客户端配置&#xff0c;默认采用DefaultClientConfigImpl实现。IRule&#xff1a;Ribbon的负载均衡策略&#xff0c;默认采用ZoneA…

【GPT4结对编程】word文档导出功能GPT4来实现

需求背景 最近产品增加了一个导出word文档的需求&#xff0c;之前有导出过pdf格式、excel格式、csv格式&#xff0c;但还没导出过word文档。 开源框架调研 我们的后端服务主要是用golang&#xff0c;因此首先想到的是golang相关的开源工具&#xff0c;找到2个。 unioffice …