数据结构(Java版)第六期:LinkedList与链表(一)

目录

一、链表

1.1. 链表的概念及结构

1.2. 链表的实现


专栏:数据结构(Java版)

个人主页:手握风云

一、链表

1.1. 链表的概念及结构

       链表是⼀种物理存储结构上⾮连续存储结构,数据元素的逻辑顺序是通过链表中的引⽤链接次序实现的。与火车类似,火车头、车厢与每一届车厢之间由火车链连接起来。在物理上,链表是不一定连续的,但在逻辑上一定是连续的。

        如下图所示,链表的结构分为两个域,一个域用来储存数据,另一个域用来储存下一个节点(类似于火车的一节车厢)的地址。 与顺序表不同的是,链表的地址在物理上不连续,但在逻辑上是连续的。最后一个节点相当于“车尾”,里面存的地址为null。这就是一个单向、不带头、非循环的链表。类似地,还有双向、带头、循环的链表。但考试考最多的说就是单链表。

      什么是带头的链表呢?如下图所示,第一个节点可以存任何数据,但存取的数据是没有意义的,唯一的作用就是起到一个“排头兵”的作用。不带头的链表呢,相当于它的head会变的,比如我们把第一个节点删掉,那么第二个节点就会成为head。

        那么什么又是循环链表呢?如下图所示,最后一个节点指向了第一个节点或第二个节点,就可以构成循环链表,但一般情况下,我们都是指向第一个节点。

1.2. 链表的实现

      接下来我们要通过代码来实现链表,我们就可以定义一个MySingleList类,链表当中有很多的节点,基于面向对象的思想,我们可以使用内部类来定义我们的节点。

public class MySingleList {static class ListNode{private int val;private ListNode next;public ListNode(int val) {this.val = val;}//因为不知道下一个节点是谁,所以这里的构造函数的参数里不写next。}public ListNode head;//表示当前链表的头节点public int listSize;
} 

       链表的基础已经写好了,下面要实行链表的增、删、查、改。我们可以把这些方法写在一个接口里面。接口里面的方法默认都是public,并且不需要具体的实现。然后我们在MySingleList类里面对这些方法进行重写。

public interface Ilist {//头插法void addFirst(int data);//尾插法void addLast(int data);//任意位置插⼊第⼀个数据节点为0 号下标void addIndex(int index,int data);//查找是否包含关键字key是否在单链表当中boolean contains(int key);public void remove(int key);//删除所有值为 key的节点public void removeAllKey(int key);//得到单链表的⻓度int size();public void clear();public void display();
}
public class MySingleList implements Ilist{static class ListNode{private int val;private ListNode next;public ListNode(int val) {this.val = val;}//因为不知道下一个节点是谁,所以这里的构造函数的参数里不写next。public ListNode head;//表示当前链表的头节点}@Overridepublic void addFirst(int data) {}@Overridepublic void addLast(int data) {}@Overridepublic void addIndex(int index, int data) {}@Overridepublic boolean contains(int key) {return false;}@Overridepublic void remove(int key) {}@Overridepublic void removeAllKey(int key) {}@Overridepublic int size() {return 0;}@Overridepublic void clear() {}@Overridepublic void display() {}
}

     我们也可以自己创建一个链表:

    public ListNode head;//表示当前链表的头节点public void CreateList(){ListNode node1 = new ListNode(11);ListNode node2 = new ListNode(22);ListNode node3 = new ListNode(33);ListNode node4 = new ListNode(44);//这些数据之间没有连续性node1.next = node2;node2.next = node3;node3.next = node4;//node4已经是最后一个节点了,不需要管最后一个nextthis.head = node1;//这样可以从第一个节点开始去遍历我们的数组}public class Main {public static void main(String[] args) {MySingleList mySingleList = new MySingleList();mySingleList.CreateList();System.out.println("=========");}
}

       我们在对象实例化这里打一个断点来进行调试。开始的时候,头节点是空的,运行到下一行时,我们的val的值和next的地址都被CreateList方法串联起来了。

        了解了next的引用原理之后,我们就可以遍历链表来对里面的数据进行打印。我们通过上面的display方法来实现,那我们如何通过head的引用从第一个节点指向第二个节点呢?

//基本变量通过自增的方式来赋值
int a = 10;
a = a + 1;//同理,引用变量也可以采用上述方法
head = head.next;
    @Overridepublic void display() {while(head != null){System.out.print(head.val+" ");head = head.next;}System.out.println();}

       我们来对这个方法进行调试一下,如下图所示,当head不为空时,进入while循环,当head.next指向第二个节点时val的值变为了22,next的地址也指向了第二个节点。当head指向最后一个节点时,地址变为null,跳出while循环。

 

    运行结果如下:

        但这种写法也有致命的缺点,如果说这个方法有返回值呢,head遍历完我们的链表之后,head引用变为了null,返回的值也会成一个null,如果我们再用ListCode创建一个cur变量,head引用保持不动,把head的引用赋值给cur,再让cur去遍历链表。

       比如我们通过size方法来获取链表的节点数,就可以这样写:

    @Overridepublic int size() {ListNode cur = head;int count = 0;while(cur != null){cur = cur.next;count++;}return count;}

     再比如我们去写contains方法去判断链表里是否存在关键字:

    @Overridepublic boolean contains(int key) {ListNode cur1 = head;while(cur1 != null){if(cur1.val == key){return true;}cur1 = cur1.next;}return false;}System.out.println(mySingleList.contains(44));System.out.println(mySingleList.contains(45));

 

        可能有的老铁在写这个方法会写出cur1.next != null,因为最后一个节点的next为null,当cur1走到最后节点时,不满足cur1.next != null,相当与根本没有遍历完这个数组。

       下面我们将要进行对链表里的数据进行增删查改。我们先来实现头插和尾插。我们如果向把一个node节点(里面存的数据是10)插入head节点前面之后,node节点就变成了head节点。我们可以通过下面两行代码来实现这个过程。这里千万不能把两行代码写反,因为这样就会使得node.next指向自己。

node.next = head;//先让node.next的地址指向node1
head = node;//再通过head引用指向node,就能把node变成头节点

    public void addFirst(int data) {ListNode node = new ListNode(data);if(head == null){head = node;//相当于插入进一个空的链表}else{node.next = head;head = node;}}public static void main(String[] args) {Ilist mySingleList = new MySingleList();mySingleList.addFirst(10);mySingleList.addFirst(20);mySingleList.addFirst(30);mySingleList.addFirst(40);mySingleList.display();}

       对于尾插的实现,与头插不同的是,我们需要先找出链表的最后一个节点,然后再让cur.next = node。如果说初始的链表是空的情况下,则cur= null,cur.next就会出现空指针异常。我们就需要参考contains方法来寻找链表的尾部。

    @Overridepublic void addLast(int data) {ListNode node = new ListNode(data);//表示链表为空if(head == null) {head = node;return;}//找到链表的尾巴ListNode cur = head;while (cur.next != null) {cur = cur.next;}cur.next = node;}

     下面我们来实现比较复杂的在任意位置插入一个节点:为了方便理解,我们给每个节点都编上号。如果说我们要把新的节点插入到2号位置,那么新的节点就会变成2号位置。但我们的cur是不能走两步的,因为插入之后2号位置不知道前面的节点是谁,这个链表是单向的,所以cur不能往回走,也就是要走index-1步。我们可以通过两行代码来实现这一过程。

node.next = cur.next;
cur.next = node;

       对于cur需要走index-1步的过程,我们可以重新写一个方法来实现。然后我们就可以把新节点插入到链表中了。

private ListNode findIndex(int index){ListNode cur = head;int count = 0;while(count != index-1){cur = cur.next;count++;}return cur;}
public void addIndex(int index, int data) {ListNode node = new ListNode(data);ListNode cur = findIndex(index);node.next = cur.next;cur.next = node;listSize++;}

       过程确实有点复杂,不懂的老铁可以去画画图去理解。到这里,看似我们的过程已经结束了,但我们需要考虑其他的一些问题。如果我们在0号位置插或者在5号位置插,那么就相当于头插和尾插了,我们就可以直接调用addFirst和addLast方法。那如果我们在-1、-2位置插呢?这时就会越界访问。我们就需要写一个方法来检查访问是否合法。

        if(index == 0) {addFirst(data);return;}if(index == size()) {addLast(data);return;}
    private void checkIndexOfAdd(int index){if(index<0 || index>size()){throw new RuntimeException("插入的位置不合法,index="+index);}}
public class IndexOutOf extends RuntimeException{public IndexOutOf() {}public IndexOutOf(String message) {super(message);}
}try {mySingleList.addIndex(6,99);}catch (IndexOutOf e) {e.printStackTrace();}

       接下来我们看删除元素。删除并不是简单的跳过这个节点,还要把要删除的节点前一个和后一个连接起来。那我们先找到要删除元素的前一个元素,我们又该如何找到要删除的节点呢?

cur.next = del.next;

rivate ListNode findNode(int key) {ListNode cur = head;while (cur.next != null) {if(cur.next.val == key) {return cur;}cur = cur.next;}return null;}

       通过上面这个方法,我们就可以找到我们要删除的节点。注意,我们不能写成cur != null,因为cur.next就会空指针异常。如果我们没有找到,就返回null。但是,我们需要考虑一下,我们要删除第一个数据,cur已经在第一个节点,那么cur.next就不会对第一个节点进行判断,从而就不会删除。

    @Overridepublic void remove(int key) {if(head == null) {return;}if(head.val == key) {head = head.next;listSize--;return;}ListNode cur = findNode(key);if(cur == null) {System.out.println("没有你要删除的数据");return;}ListNode del = cur.next;cur.next = del.next;listSize--;}

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

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

相关文章

从零开始C++棋牌游戏开发之第三篇:游戏的界面布局设计

在游戏开发的旅途中&#xff0c;界面布局设计是一个充满创意和挑战的环节。对于棋牌类游戏而言&#xff0c;界面不仅仅是功能的载体&#xff0c;更是玩家与游戏互动的桥梁。一个清晰、直观且美观的界面可以显著提升游戏的用户体验。 在这篇文章中&#xff0c;我们将从功能需求…

计算机基础知识——数据结构与算法(五)(山东省大数据职称考试)

大数据分析应用-初级 第一部分 基础知识 一、大数据法律法规、政策文件、相关标准 二、计算机基础知识 三、信息化基础知识 四、密码学 五、大数据安全 六、数据库系统 七、数据仓库. 第二部分 专业知识 一、大数据技术与应用 二、大数据分析模型 三、数据科学 数据结构与算法…

数据库管理-第275期 Oracle 23ai:画了两张架构图(20241225)

数据库管理275期 2024-12-25 数据库管理-第275期 Oracle 23ai&#xff1a;画了两张架构图&#xff08;20241225&#xff09;1 系统管理分片2 用户定义分片总结 数据库管理-第275期 Oracle 23ai&#xff1a;画了两张架构图&#xff08;20241225&#xff09; 作者&#xff1a;胖…

增强路由器 路由器升级宽带速度

由器中DNS设置 DNS&#xff08;域名系统&#xff09;是什么&#xff1f; DNS将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便地访问互联网。DNS使用UDP端口53。 上网前提是&#xff1a;配置 IPv4地址、子网掩码 、网关、DNS 正确才能够上网 DNS填写规…

stm32制作CAN适配器5--WinUsb上位机编写

上次我们要stm32制作了一个基于winusb有canfd适配器&#xff0c;今天我们来制作一个上位机程序来进行报文收发。 上位机还是用以前写好的&#xff0c;只是更改下dll文件。 项目链接器&#xff0c;输入&#xff0c;附加依赖项中增加winusb.lib winusb初始化&#xff1a;#incl…

谷歌浏览器 Chrome 提示:此扩展程序可能很快将不再受支持

问题现象 在Chrome 高版本上的扩展管理页面&#xff08;地址栏输入chrome://extensions/或者从界面进入&#xff09;&#xff1a; &#xff0c; 可以查看到扩展的情况。 问题现象大致如图: 问题原因 出现此问题的根本原因在于&#xff1a;谷歌浏览器本身的扩展机制发生了…

Vue.js组件(6):echarts组件

1 前言 本章主要对常用的echars图表展示进行基本的组件封装。使用该组件前需要在项目中引入echarts。官网&#xff1a;Apache ECharts npm install echarts --save 2 图表组件 2.1 折线图组件 组件属性&#xff1a;chartId&#xff0c;指定图表挂载div的id&#xff0c;注意不…

MySQL用表组织数据

用表组织数据 文章目录 用表组织数据一.四种完整性约束二.数值类型2-1三.数值类型2-2四.字符串.日期类型五.设置1.设置主键2.设置标识列3.设置非空4.设置默认值 六.主外键建立后注意事项 一.四种完整性约束 1.域完整性 列 域完整性约束方法:限制数据类型,检查约束,外键约束,默…

面试经典问题 —— 最大/小前K个数问题(top - K)问题

目录 常见思路更优的解法&#xff08;面试官喜欢的&#xff09; 常见思路 要选出最小的前K个数首先我们会想到排排升序建大堆&#xff0c;排降序建小堆 一个直观的想法是使用&#xff08;小根堆&#xff09;&#xff0c;起始将所有元素放入堆中&#xff0c;然后再从堆中取出k 个…

外包干了27天,技术退步明显。。。。。

时光荏苒&#xff0c;转眼我已是一个拥有近四年功能测试经验的大专生。20年&#xff0c;我满怀激情地通过校招进入湖南某知名软件公司&#xff0c;期待在这里开启我的职业生涯。然而&#xff0c;长时间的舒适环境让我渐渐失去了前进的动力&#xff0c;技术停滞不前&#xff0c;…

从自动驾驶到具身智能漫谈

0. 简介 从作者的眼光来看自动驾驶和具身智能已经是越来越接近了。无论是技术栈以及实现的最终目的。其实都是希望人在环内。这个是古月直播的文字相关的大致梳理。主要会展开聊一聊自动驾驶的变迁以及作为自动驾驶的从业人员要着重关注的一些技术点 1. 自动驾驶的变迁 在自…

Excel粘贴复制不完整的原因以及解决方法

在数据处理和分析的过程中&#xff0c;Excel无疑是不可或缺的工具。然而&#xff0c;在使用Excel进行复制粘贴操作时&#xff0c;有时会遇到粘贴不完整的情况&#xff0c;这可能会让人感到困惑和烦恼。本文将深入探讨Excel粘贴复制不完整的原因、提供解决方案&#xff0c;并给出…

云原生之docker详解

目录 1.云原生概念 1.1 云原生定义 1.2 云原生元素 1.2.1 微服务 1.2.2 DevOps 1.2.3 持续交付 1.2.4 容器化 2. Docker 2.1 Docker概述 2.1.1 Docker 定义 2.1.2 Docker应用场景 2.1.3 Docker的架构 2.2 Docker命令 2.2.1 docker进程相关命令 2.2.2 docker镜像…

数仓开发那些事(8)

程序员圣经 为什么刚刚能运行&#xff0c;现在就不行 为什么刚刚不运行&#xff0c;现在就可以 为什么他的可以跑&#xff0c;我的不能跑 为什么我的可以跑&#xff0c;他的就不行 为什么这台电脑能&#xff0c;那台就不行 为什么这台电脑不行&#xff0c;那台就行 神州员工&a…

在UE5中调用ImGui图形界面库

ImGui是一个小巧灵活、简洁美观的图形界面库 首先我们直接参考Github https://github.com/SLSNe/Unreal5-ImGui 把项目下载下来后 打开项目目录或者引擎目录 项目根目录/Plugins/ImGui/ 或 UE5引擎根目录/Engine/Plugins/ 如果没有Plugins文件夹就新建一个 把项目放里面…

华为管理变革之道:奋斗文化与活力

目录 企业文化是什么&#xff1f; 为什么活下去是华为的文化&#xff1f; 活下来&#xff0c;是华为公司的最低纲领&#xff0c;也是华为公司的最高纲领&#xff01; 资源终会枯竭&#xff0c;唯有文化才能生生不息 企业文化之一&#xff1a;以客户为中心 企业文化之二&a…

JZ31 栈的压入、弹出序列

题目来源&#xff1a;栈的压入、弹出序列_牛客题霸_牛客网 题目&#xff1a;如下 输入两个整数序列&#xff0c;第一个序列表示栈的压入顺序&#xff0c;请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序&#xf…

(echarts)数据地图散点类型根据条件设置不同的标记图片

(echarts)数据地图散点类型根据条件设置不同的标记图片 1.用在线工具将本地图片转化base64格式 data(){return { base64Img:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADQ...",} }在线转换地址&#xff1a;https://www.jyshare.com/front-end/59/ 2.symbol属…

ArcGIS+MIKE21 洪水淹没分析、溃坝分析,洪水淹没动态效果

洪水淹没分析过程&#xff1a; 一、所需数据&#xff1a; 1.分析区域DEM数据 二、ArcGIS软件 1.提取分析区域DEM&#xff08;水库坝下区域&#xff09; 2.DEM栅格转点 3.计算转换后几何点的x和y坐标值&#xff08;精度20、小数位3&#xff09; 4.导出属性表&#xff0c;形式…

LSTM-SVM时序预测 | Matlab基于LSTM-SVM基于长短期记忆神经网络-支持向量机时间序列预测

LSTM-SVM时序预测 | Matlab基于LSTM-SVM基于长短期记忆神经网络-支持向量机时间序列预测 目录 LSTM-SVM时序预测 | Matlab基于LSTM-SVM基于长短期记忆神经网络-支持向量机时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.LSTM-SVM时序预测 | Matlab基于LSTM…