Java:链表

一、链表简介

1、链表与顺序表的区别

上一篇博客我介绍了顺序表,这次我们来认识认识链表!先来看看二者的区别:

顺序表:由于顺序表实际上是一个数组,因此它在物理上是连续的,逻辑上也是连续的!

链表    :链表实际上是由一块一块的内存组成,因此物理上表示连续的,但是在逻辑上是连续的!

链表的一个生动例子也就是我们日常出行可见的火车高铁!大家想想看:火车是不是有一节一节的车厢连接而成?在就很我们要学习的链表很相似了!链表也是由一块一块的内存连接而成!

如图:

链表是由一个一个的节点组织起来的,整体就叫做链表



2、链表分类:

分类的几种标准:

单向           双向

带头           不带头

循环           非循环 

根据以上标准,链表基本划分为八种:

单向带头循环、单向带头非循环、单向不带头循环、单向不带头非循环

双向带头循环、双向带头非循环、双向不带头循环、双向不带头非循环

全部讲解不太现实,也没有必要,我们只要认识两种比较重要的类型就行了!接下来我们主要认识的是:

单向不带头非循环、双向不带头非循环



3、通过画图认识几种类型:

单向不带头非循环:

如图:

1、每一个节点由两个域构成,分别是valuenext,value存储这个节点的数据,next存储下一个节点的地址!

2、单向的意思是这个链表只有一个方向,即箭头方向

3、不带头表示表示这个链表没有头,而是指这个链表的头不固定!

如这个例子,现在这个头的地址为第一个节点的地址0x12,如果把第一个节点删除,那么这个头的地址也就修改为第二个节点的地址0x33



单向带头非循环: 

 与前面的单向不带头非循环对比,区别是这个类型带了一个固定的头,它永远不会变!

如图:



循环类型: 

循环类型与非循环类型的区别在于,最后一个节点存储的地址是不是为null?

非循环:最后一个节点存储的地址为null

循环   :最后一个节点存储的地址为第一个节点的地址 

如图:这个为循环类型



二、链表的实现

全部代码:

1、链表类代码:

public class MySingleLinkedList {//链表是由一个个节点构成,因此我们可以抽象出一个节点类class ListNode{public int val;public ListNode next;//创建构造方法public  ListNode(int val){this.val=val;}}//链表的头节点public ListNode head;//创建一个小链表的方法:public void create(){ListNode node1=new ListNode(1);ListNode node2=new ListNode(2);ListNode node3=new ListNode(3);ListNode node4=new ListNode(4);//通过地址连接链表node1.next=node2;node2.next=node3;node3.next=node4;head=node1;}//打印链表的方法:public void display(){ListNode cur=head;while(cur!=null){System.out.print(cur.val+" ");cur=cur.next;}}//求链表长度的方法:public int size(){ListNode cur=head;int count=0;while(cur!=null){count++;cur=cur.next;}return count;}//头插的方法:public void addFirst(int val){ListNode node=new ListNode(val);node.next=head;head=node;}//尾插的方法:public void addLast(int val){ListNode node=new ListNode(val);//如果head为空if(head==null){head=node;return ;}//当head不为空ListNode cur=head;//找到尾节点while(cur.next!=null){cur=cur.next;}cur.next=node;}//任意位置插入的方法:public void addIndex(int index,int val){//检查index是否合法try{checkIndex(index);}catch (IndexNotLeaglException e){e.printStackTrace();}//index==0,相当于头插if(index==0){addFirst(val);return;}//index=size,相当于尾插if(index==size()){addLast(val);return;}//0<index<sizeif(index>0&&index<size()){ListNode node=new ListNode(val);ListNode cur=findIndexSubOne(index);node.next=cur.next;cur.next=node;}}//找到index的前一个位置的方法:private ListNode findIndexSubOne(int index){ListNode cur=head;int count=0;while(count!=index-1){cur=cur.next;count++;}return cur;}//检查index是否合法private void checkIndex(int index){if(index<0||index>size()){throw new IndexNotLeaglException("index不合法!");}}//查找是否包含某个关键字key的方法:public boolean contains(int key){ListNode cur=head;while(cur!=null) {if(cur.val==key){return true;}cur=cur.next;}return false;}//删除链表中第一次出现关键字key的节点方法:public void remove(int key){//1、删除头节点if(head==null){return;}if(head.val==key){head=head.next;return;}//2、要删除的元素不在头节点//先找到要删除的前一个节点ListNode cur=head;while(cur.next!=null){if(cur.next.val==key){break;}cur=cur.next;}//删除该节点cur.next=cur.next.next;}//去除链表中所有相同的关键字public void removeAllKey(int key){if(head==null){return;}//1、先去除头节点以外的与key相同值的节点ListNode prev=head;//cur的前驱节点ListNode cur=head.next;//寻找要去除的节点//判断并且删除节点while(cur!=null){if(cur.val==key){prev.next=cur.next;cur=cur.next;}else{prev=cur;cur=cur.next;}}//2、判断头节点存储的内容是否为keyif(head.val==key){head=head.next;}}
}

2、异常类代码: 

public class IndexNotLeaglException extends RuntimeException{IndexNotLeaglException(){}IndexNotLeaglException(String msg){super(msg);}
}

全部类文件: 




  代码讲解: 

1、类文件

如图,为了实现链表,我们创建了两个类:MySingleLinkedList类和Test类

MySingleLinkedList类:主要是用来创建链表和实现链表的具体功能!

Test类:主要是用来测试和使用链表



2、创建节点类 

   我们知道,链表是由一个一个的节点构成的,每个节点由value(节点的内容)next(下一个节点的地址)构成,那么在自己实现链表的过程中,我们就可以抽象出来一个节点类,包含两个成员变量val和next。

   此时这个ListNode类定义在MySingleLinkedList类的内部,称为内部类!

   另外,由于链表需要一个头,方便我们遍历链表,因此我们可以定义一个ListNode类型的引用变量head,用来存储第一个节点的地址!

public class MySingleLinkedList {//链表是由一个个节点构成,因此我们可以抽象出一个节点类class ListNode{public int val;public ListNode next;//创建构造方法public ListNode(int val){this.val=val;}}//链表的头节点public ListNode head;}


3、创建链表方法: 

我们知道,链表的底层不像顺序表,它不是由数组构成,而是由一个一个的节点连接而成!

因此我们在创建链表的时候,需要实例化一个一个的ListNode类!这里我们可以实现一个方法,帮助我们创建一个小链表! 

//创建一个小链表public void create(){ListNode node1=new ListNode(1);ListNode node2=new ListNode(2);ListNode node3=new ListNode(3);ListNode node4=new ListNode(4);//通过地址连接链表node1.next=node2;node2.next=node3;node3.next=node4;head=node1;}


3、打印链表方法:

为了知道链表中存储了什么内容,我们可以实现一个打印链表的方法!

//打印链表方法:public void display(){ListNode cur=head;while(cur!=null){System.out.print(cur.val+" ");cur=cur.next;}}


4、求链表长度方法: 

 //求链表长度public int size(){ListNode cur=head;int count=0;while(cur!=null){count++;cur=cur.next;}return count;}


 5、头部插入方法

在第一个节点之前插入一个新的节点:

首先,我们需要新实例化一个ListNode类node作为一个新的节点,此时的node.next

里面存储

插入前:

插入后:

 //头插public void addFirst(int val){ListNode node=new ListNode(val);node.next=head;head=node;}


6、尾部插入方法: 

尾部插入方法分为两种情况讨论:链表head为空or链表head不为空 

首先实例化一个节点类的对象node

1、当链表的head为空,我们只需要将head=node就行

2、当链表的head不为空,先找到尾节点,再将尾节点的next赋值为node

 //尾插public void addLast(int val){ListNode node=new ListNode(val);//如果head为空if(head==null){head=node;return;}//当head不为空ListNode cur=head;//找到尾节点while(cur.next!=null){cur=cur.next;}cur.next=node;}


7、任意位置插入方法: 

   相对于头部插入尾部插入,这个任意位置插入的方法实现起来比较复杂,原因是,它自己本身也包含了头部插入和尾部插入功能!并且在此之上,还可以插入除了头部和尾部之间的任意位置! 

   首先,我们要弄清楚的一点是,链表本身不像顺序表,它是没有下标这个概念的,但是为了实现这个功能,我们可以人为地认为链表是有下标(index)的!

如图:

那么我们可以将其分为几种情况:

1、index==0,这时相当于头部插入

2、index==size(size表示链表节点的个数),这时相当于尾部插入

3、0<index<size,插入除头部和尾部的任意中间位置

4、index<0或index>size,下标不合法


1、index==0 和  2、index==size

这个时候调用头部插入方法和尾部插入方法即可

        //index==0,相当于头插if(index==0){addFirst(val);return;}//index=size,相当于尾插if(index==size()){addLast(val);return;}

3、0<index<size 

这种情况下,我们分为两步走:

第一步:找到index位置的前一个位置,

先定义一个cur==head,然后移动cur,使cur找到index位置的前一个位置

  //找到index的前一个位置的方法:private ListNode findIndexSubOne(int index){ListNode cur=head;int count=0;while(count!=index-1){cur=cur.next;count++;}return cur;}

第二步:连接节点 

连接节点只需要修改两个东西,以图为例子:

一个是新节点node的next,要存入原下标为2的节点的地址

一个是下标为1的节点的next,要修改为node的地址

修改之后:

 //0<index<sizeif(index>0&&index<size()){ListNode node=new ListNode(val);ListNode cur=findIndexSubOne(index);node.next=cur.next;cur.next=node;}

 4、index<0或index>size(index不合法)

这种情况下,我们可以在插入前检查下标,如果不合法,那就抛出异常!

首先新建一个类,定义一个异常类:

public class IndexNotLeaglException extends RuntimeException{IndexNotLeaglException(){}IndexNotLeaglException(String msg){super(msg);}
}

然后在MySingleLinkedList类中实现一个检查下标的方法:

//检查index是否合法private void checkIndex(int index){if(index<0||index>size()){throw new IndexNotLeaglException("index不合法!");}}

接下来就使用try……catch语句使用该异常类 

 



任意插入方法的全部代码  

//任意位置插入的方法:public void add(int index,int val){//检查index是否合法try{checkIndex(index);}catch (IndexNotLeaglException e){e.printStackTrace();}//index==0,相当于头插if(index==0){addFirst(val);return;}//index=size,相当于尾插if(index==size()){addLast(val);return;}//0<index<sizeif(index>0&&index<size()){ListNode node=new ListNode(val);ListNode cur=findIndexSubOne(index);node.next=cur.next;cur.next=node;}}//找到index的前一个位置的方法:private ListNode findIndexSubOne(int index){ListNode cur=head;int count=0;while(count!=index-1){cur=cur.next;count++;}return cur;}//检查index是否合法private void checkIndex(int index){if(index<0||index>size()){throw new IndexNotLeaglException("index不合法!");}}
}

新创建的异常类型: 



8、查找是否包含关键字key的方法:

查找链表中的每一个节点,如果链表中存在key,返回true,不存在key,返回false 

 //查找是否包含某个关键字key的方法:public boolean contains(int key){ListNode cur=head;while(cur!=null) {if(cur.val==key){return true;}cur=cur.next;}return false;}

9、删除第一次出现关键字为key的节点 的方法:

   删除指定元素的节点分为2种情况:

1、删除的元素在头节点:

这种情况比较简单:只需要将链表的头修改为下一个链表的地址即可!

另外,如果head==null,不执行任何操作,直接return

 //1、删除头节点if(head==null){return;}if(head.val==key){head=head.next;return;}

2、要删除的元素不在头节点: 

如果要删除指定元素所在的节点,我们可以先定义一个cur,让它找到要删除节点的前一个节点

   用cur.next节点的val来判断,cur的下一个节点存储的元素是不是我们要找的元素key,如果是,那么此时的cur正指向要删除节点的前一个节点

   注意,此时while循环的判断条件应该是(cur.next!=null)而不是(cur!=null)!

//先找到要删除的前一个节点ListNode cur=head;while(cur.next!=null){if(cur.next.val==key){break;}cur=cur.next;}//删除该节点cur.next=cur.next.next;}

让我们看看二者的区别:

1、cur!=null

当cur指向该链表的最后一个节点,由于cur!=null,因此进入循环。

我们就会发现,在循环体语句cur.next.val==key会报错!因为cur.next为null,因此也不会有cur.next.next(相当于null.next)

2 、cur.next!=null

当cur指向倒数第二个节点,上述的语法不会报错!



10、删除链表中相同的元素的方法:

//去除链表中所有相同的关键字public void removeAllKey(int key){if(head==null){return;}//1、先去除头节点以外的与key相同值的节点ListNode prev=head;//cur的前驱节点ListNode cur=head.next;//寻找要去除的节点//判断并且删除节点while(cur!=null){if(cur.val==key){prev.next=cur.next;cur=cur.next;}else{prev=cur;cur=cur.next;}}//2、判断头节点存储的内容是否为keyif(head.val==key){head=head.next;}}

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

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

相关文章

【IDEA】使用debug方式去运行java程序

什么是debug工具&#xff1f; 调试工具&#xff08;debug工具&#xff09;是一种用于帮助程序员识别和修复程序中的错误的工具。它们提供了一系列的功能&#xff0c;帮助程序员在代码执行的过程中跟踪和检测问题&#xff0c;例如查看变量的值、检查函数的调用栈、设置断点来停…

Spring学习——什么是循环依赖及其解决方式

文章目录 前言一、什么是循环依赖二、解决思路1、循环依赖分类2、对象初始化步骤及对象分类3、spring是如何解决的4、图解5、三级缓存1、区别2、ObjectFactory是什么 三、源码debug1、spring创建对象过程1、dubug第一步——找到getBean2、dubug第二步——getBean与doGetBean3、…

腾讯 tendis 替代 redis linux安装使用

下载地址 Tendis存储版 点击下载 linux 解压 tar -zxvf 安装包.tgz cd 解压安装包/scripts 启动 ./start.sh 停止 ./stop.sh 详细配置 修改 /scripts tendisplus.conf # tendisplus configuration for testing # 绑定本机IIP bind 192.168.31.112 port 51002 #设…

海格里斯助推实体制造业转型升级 “算法定义硬件”解题AIoT市场

随着自动化的发展&#xff0c;电子商务和智能制造推动了自动化立体仓库的快速发展与创新&#xff0c;产生了“密集仓储”的概念。对于一个实体企业来讲&#xff0c;其数智物流转型正在趋向于“去伪存真”&#xff0c;企业追求高ROI与真实经济价值&#xff0c;具有降本增效的业务…

JavaEE 初阶篇-深入了解多线程安全问题(出现线程不安全的原因与解决线程不安全的方法)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 多线程安全问题概述 1.1 线程不安全的实际例子 2.0 出现线程不安全的原因 2.1 线程在系统中是随机调度且抢占式执行的模式 2.2 多个线程同时修改同一个变量 2.3 线…

游戏行业行业竞争越来越激烈,遇到DDoS攻击遭受严重损失该如何解决

近年来&#xff0c;我们见证了数字化的快速发展&#xff0c;随着这样的发展&#xff0c;网络的威胁也逐渐增多&#xff0c;在网络攻击门槛不断降低&#xff0c;行业竞争越来越激烈&#xff0c;游戏行业的DDoS攻击如雨点般密集&#xff0c;在整个DDoS攻击的份额中&#xff0c;游…

SpringAMQP-Exchange交换机

1、Fanout-Exchange的特点是&#xff1a;和它绑定的消费者都会收到信息 交换机的作用是什么? 接收publisher发送的消息将消息按照规则路由到与之绑定的队列不能缓存消息&#xff0c;路由失败&#xff0c;消息丢失FanoutExchange的会将消息路由到每个绑定的队列 声明队列、交…

【实验报告】--基础VLAN

【VLAN实验报告】 一、项目背景 &#xff08;为 Jan16 公司创建部门 VLAN&#xff09; Jan16 公司现有财务部、技术部和业务部&#xff0c;出于数据安全的考虑&#xff0c;各部门的计算机需进 行隔离&#xff0c;仅允许部门内部相互通信。公司拓扑如图 1 所示&#xff0c; …

http和https的工作原理是什么?

HTTP&#xff08;HyperText Transfer Protocol&#xff09;和HTTPS&#xff08;HyperText Transfer Protocol Secure&#xff09;是两种用于在互联网上传输数据的主要协议&#xff0c;它们均用于在客户端&#xff08;通常是Web浏览器&#xff09;与服务器之间交换信息。尽管它们…

.NET CORE 分布式事务(二) DTM实现TCC

目录 引言&#xff1a; 1. TCC事务模式 2. TCC组成 3. TCC执行流程 3.1 TCC正常执行流程 3.2 TCC失败回滚 4. Confirm/Cancel操作异常 5. TCC 设计原则 5.1 TCC如何做到更好的一致性 5.2 为什么只适合短事务 6. 嵌套的TCC 7. .NET CORE结合DTM实现TCC分布式事务 …

访学博后须知|携带手机等电子产品入境美国注意事项

美国对携带手机等电子产品入境有着严格的规定&#xff0c;因此知识人网小编提醒拟出国做访问学者、博士后或联合培养的博士生了解以下注意事项&#xff0c;尽量减少不必要的麻烦。 随着互联网的普及&#xff0c;手机等电子产品在人民生活中占有不可或缺的地位。因为研究和工作需…

海量数据处理项目-账号微服务和流量包数据库表+索引规范(下)

海量数据处理项目-账号微服务和流量包数据库表索引规范&#xff08;下&#xff09; 第2集 账号微服务和流量包数据库表索引规范讲解《下》 简介&#xff1a;账号微服务和流量包数据库表索引规范讲解 账号和流量包的关系&#xff1a;一对多traffic流量包表思考点 海量数据下每…

ES6 学习(二)-- 字符串/数组/对象/函数扩展

文章目录 1. 模板字符串1.1 ${} 使用1.2 字符串扩展(1) ! includes() / startsWith() / endsWith()(2) repeat() 2. 数值扩展2.1 二进制 八进制写法2.2 ! Number.isFinite() / Number.isNaN()2.3 inInteger()2.4 ! 极小常量值Number.EPSILON2.5 Math.trunc()2.6 Math.sign() 3.…

仓库规划(plan)

明天就要考试了&#xff0c;但是我正处于一点都不想学的状态 高考前我也是这样的 逆天 代码如下&#xff1a; #include<vector> #include<cstdio> using namespace std; int n, m; struct Node{int id;vector<int> d;bool operator<(const Node &t…

平台介绍-搭建赛事运营平台(8)

平台介绍-搭建赛事运营平台&#xff08;5&#xff09;提到了字典是分级的&#xff0c;本篇具体介绍实现。 平台级别的代码是存储在核心库中&#xff0c;品牌级别的代码是存储在品牌库中&#xff08;注意代码类是一样的&#xff09;。这部分底层功能封装为jar包&#xff0c;然后…

matlab BP神经网络回归预测(套用任何数据不改代码,最后一列是标签)

部分代码&#xff1a; %% BP神经网络 % 清空环境变量 关闭打开过的图表 clear all;clc;close all %% 导入数据 dataxlsread(data1.xlsx); %% 设置训练集数量 num_rowsize(data,1) %数据集行数 n_trainsfloor(num_row*0.8) %按比例求训练集数目 % n_trains150 …

JDK和IntelliJ IDEA下载和安装及环境配置教程

一、JDK下载&#xff08;点击下方官网链接&#xff09; Java Downloads | Oracle 选择对应自己电脑系统往下拉找到自己想要下载的JDK版本进行下载&#xff0c;我下的是jdk 11&#xff0c;JDK有安装版和解压版&#xff0c;我就直接下安装版的了。 .exe和.zip的区别&#xff1a…

通过MobaXterm工具可视化服务器桌面

一、MobaXterm工具 MobaXterm是一款功能强大的远程连接工具&#xff0c;可以连接到各种类型的服务器&#xff0c;包括Linux、Windows和MacOS。支持多种协议&#xff0c;包括SSH、RDP、VNC和Telnet MobaXterm可以通过X11转发功能可视化服务器桌面。 二、MobaXterm工具可视化服务…

011——人体感应模块驱动开发(SR501)

目录 一、 模块简介 二、 工作原理 三、 软件及验证 一、 模块简介 人体都有恒定的体温&#xff0c;一般在 37 度&#xff0c;所以会发出特定波长 10uM 左右的红外线&#xff0c;被动式红外探头就是靠探测人体发射的 10uM 左右的红外线而进行工作的。 人体发射的 10…

架构师之路--Docker的技术学习路径

Docker 的技术学习路径 一、引言 Docker 是一个开源的应用容器引擎&#xff0c;它可以让开发者将应用程序及其依赖包打包成一个可移植的容器&#xff0c;然后在任何支持 Docker 的操作系统上运行。Docker 具有轻量级、快速部署、可移植性强等优点&#xff0c;因此在现代软件开…