图解&代码实现:单向链表的创建(直接添加到链表的尾部,不考虑排序)
链表的基本介绍
链表是有序列表,链表的英文是
LinkedList
链表的
特点:
-
链表是以结点方式来存储的,链表是链式存储
-
链表中的每个结点都分data域和next域
data域 : 用来存放数据
next域:用来指向下一个结点
下面展示的是 链表再内存中的
实际结构
的布局图:
-
链表的各个结点不一定是连续存储的
-
链表分为带头结点的链表和不带头结点的链表,具体要不要带头结点,根据自己的需求而定
链表的实际案例
实现:管理在线的用户
需求:在某个约定时间,把某个人的好友发送给服务器,但是发送过去的好友编号并不是连续的,要求在服务器上把所有的好友信息按照编号的顺序返回给客服端,因为客服端要定时地向服务器去询问其好友的状态,而服务器需将客服端发送过来的好友按照编号顺序返回,比如说客服端发送过来某一个人的好友编号是 20、1、19、38、5,发给服务器后,要求把这个人的状态返回给客服端,但是要求返回的好友编号顺序是按照从小到大的顺序排列的,比如 1 、 5、19 、20、38,并且不允许查数据库,不能将数据存入数据库中,然后通过order by返回,这样是不允许的。要求在内存中就将这个排序工作做完,有时间和速度要求。
解决方法:使用链表来实现
实现思路:获取到好友Id编号后,按照顺序插入到单向链表中去
带头结点的单链表的
逻辑结构,如下图所示
根据上图做描述:头结点指向
a1,
a1执行
a2,
a2指向
a3,依此类推,若链表中最后一个结点的next域为null就代表链表结束了。
从上图看出
a1后面就是
a2,a2后面就是
a3,依此类推。但这只是逻辑结构,之所以这样画是因为
a1的下一个next域正好是指向
a2的,但实际上在内存中,
a1后面并不一定马上指的就是
a2,a1的next域只是通过指针指向了
a2的一个地址,最后将它们连成了一个单链表。该图链表中的各个结点看起来好像是连续存储的,但实际上不是。在做开发做分析时,往往都是这样画链表。实际的存储如下图所示:
单链表的应用实例
使用带head头结点的单向链表实现实现水浒英雄排行榜的管理,即:用链表来管理一些英雄人物
目标:完成对英雄人物的增删改查,也就是对结点的增删改查的操作
创建单链表的形式:
-
直接添加在链表尾部,不考虑排序
即:给我一个英雄对象,我就把它加入到链表最后,不考虑排序
-
在添加结点时,考虑排序
即:在添加英雄时,考虑其排名
创建单链表的示意图(添加) 和 遍历单链表的思路分析:
何为创建?即 添加,添加结点的同时产生链表。
注意:
- head头结点不存放具体的数据
- head头结点的作用:就是用来连接整个链表,表示单链表的头
根据上图,分析创建单链表的思路:
当我创建一个新的
HeroNode结点,然后让头结点的next域指向这个新建的
HeroNode结点,这个新建的
HeroNode结点中有两个部分,分别具体的数据(即:data域)和next域,这个新建的
HeroNode结点的next域又指向下一个
HeroNode2结点,若还有一个
HeroNode3结点,则让前一个
HeroNode2结点的next域指向后一个
HeroNode3结点。这样就形成了一个链表。若最后一个
HeroNode3结点不再指向任何结点了,那这最后一个
HeroNode3结点的next域默认为
null
总结创建(添加)单链表步骤:
第一步,先创建一个head结点,作用就是用来连接整个链表,表示单链表的头
第二步,后面每添加一个结点,就直接加入到链表的最后
问题:
-
怎么去判断链表中的哪一个是最后的结点呢?
答:是以判断结点的next域是否为空来决定这个链表是否结束
遍历显示单链表的思路分析:
遍历时,通过定义一个临时变量(或者叫做辅助指针),帮助遍历整个单链表,因为head结点不能动,若head头结点变化了,就找不到链表最后那个结点了.
用代码分析实现单向链表
链表最重要的就是
增删改查
-
首先定义一个
HeroNode
类,每个HeroNode
对象就是一个结点代码实现:
class HeroNode { // 编写属性(共4个) // data域 用来存放具体的数据 public int id; public String name; public String nickName; // next域 用来指向下一个结点 public HeroNode next; // 最重要的一个属性 // 无参构造 public HeroNode() { } // 带参构造,实现将局部变量的值赋值给成员变量 public HeroNode(int id,String name,String nickName) { // 对属性进行初始化 this.id = id; this.name = name; this.nickName = nickName; } // 重写toString()方法,为了显示 | 遍历方便 @Override public String toString() { return "HeroNode{" + "id=" + id + ", name='" + name + '\'' + ", nickName='" + nickName + '\'' + '}'; } }
-
创建单链表
SingleLinkedList
,用来管理英雄人物,即:创建单链表来操作结点,问题:
应该怎么把结点添加到单向链表中呢?
(不考虑编号顺序)
答:添加时,最重要的是每添加一个结点,直接添加到链表的最后,假如要添加新结点,就
一定
要想办法找到链表的最后一个结点。打个比方,如下图:
添加结点到单项链表的思路总结:(不考虑编号顺序)
- 找到当前链表的最后一个结点
- 将最后一个结点的next域指向要添加的结点(即:新结点)即可
代码展示:
class SingleLinkedList { // 先初始化一个头结点head,头结点不要动,头结点不存放具体的数据,作用:用来连接整个链表,表示单链表的头 // 初始化HeroNode private HeroNode head = new HeroNode(0,"",""); // 编写成员方法addNode(HeroNode heroNode),实现添加结点到单向链表 public void addNode(HeroNode heroNode){ // 无返回值带参 // 因为head结点不能动,因此需要一个临时变量(或者叫做辅助指针) temp HeroNode temp = head; // 通过死循环遍历链表,找到链表中的最后一个结点 while(true){ if(temp.next == null){ // 找到链表的最后最后一个结点了,因为temp.next = null 了 break; } // 若没有找到最后一个结点就将temp后移,就将temp后移,继续找 temp = temp.next; } // 当退出while循环时,说明找到链表的最后一个结点,temp也指向了最后一个结点。 // 将最后一个结点的next域指向要添加的结点(即:新结点)即可 temp.next = heroNode; } // 编写成员方法showList() , 实现显示链表,通过遍历完成 public void showList() { // 判断链表是否为空 if(head.next == null) { // 说明链表为空 System.out.println("链表为空"); return; } // 1. 因为头结点head不能动,因此需要一个临时变量(或者叫做辅助指针)来遍历 // 链表不为空,说明链表中至少有一个数据 HeroNode temp = head.next; while(true){ // 2. 判断是否遍历到了链表的最后一个结点 if(temp == null) { // 说明已遍历到链表最后一个结点,temp = null,后面就没有结点可以遍历了 break; } // 3. 不为空(也就是还没有遍历到链表的最后一个结点),输入结点信息 System.out.println(temp); // 重写了toString方法的,会打印整个结点信息 // 4. 将temp后移,若不后移会造成死循环,一定记住。 temp = temp.next; } } }
实现添加结点到单向链表addNode(HeroNode heroNode)
时的问题:
-
如何描述这
HeroNode temp = head;
行代码的意义?答:有个temp临时变量指向了head头结点,因为head结点不能动,若head结点变化了,那就找不到链表最后的那一个结点了,所以需要这个临时变量.
-
如何通过temp结点找到链表中的最后一个结点呢?
答:通过遍历即可
画图分析:
-
什么时候说明找到链表的最后一个结点了?(
重要
)答:当
temp.next = null
时,说明temp找到了链表中的最后一个结点。比方说:假设链表中只有一个head头结点,则head头结点就是该链表中的最后一个结点,若不是则继续找 -
如何来解释
temp = temp.next
这行代码?答:可以理解为若temp没有指向链表中的最后一个结点,或者说若temp没有找到找到链表中的最后一个结点,就将temp后移
-
如何解释将
temp
后移?画图并描述:
描述:以上图链表而言,若temp所指向的链表中的第一个结点head不是最后一个结点,就将temp往后移动一下
temp = temp.next
,若temp所指向的链表中的第二个结点HeroNode
还不是最后一个结点,就再次将temp往后移动一下temp = temp.next
,若temp所指向的链表中的第三个结点HeroNode2
还不是最后一个结点,就再将temp往后移动一下temp = temp.next
,依此类推,直到temp指向链表中最后一个结点HeroNode3
时发现:HeroNode3
这个结点的next域为空temp.next = null
,然后就将链表中的最后一个结点HeroNode3
的next域指向要添加的结点HeroNode4
(即:新结点)temp.next = HeroNode4
.
**实现遍历显示单链表时
showList()**的问题:
-
什么时候链表为空?
答:当
head.next = null
时,说明链表为空 -
如何描述这
HeroNode temp = head.next;
行代码的意义?答:因为头结点head不能动,因此需要一个临时变量(或者叫做辅助指针)来遍历,链表不为空,说明链表中至少有一个数据
-
什么时候说明遍历到链表最后一个结点了?
答:当
temp = null
时,说明已遍历到链表最后一个结点,temp = null,后面就没有结点可以遍历了。 -
为什么要执行
temp = temp.next;
,为什么将temp后移?画图并描述:
描述:因为在遍历显示链表时,输出了链表中一个结点
HeroNode
的数据后,要也要继续输出下一个结点HeroNode2
的数据,依次类推,直到遍历输出到链表中最后一个结点HeroNode4
时,说明最后一个结点HeroNde4
的next域为null了,也就是temp = null
了,就break
跳出循环 ,所以需要将temp后移,若不后移则是死循环.
编写测试类,测试一下
代码展示:
public class SingleLinkedListTest { public static void main(String[] args) { // 先创建4个结点 HeroNode heroNode = new HeroNode(1,"宋江","及时雨"); HeroNode heroNode2 = new HeroNode(2,"卢俊义","玉麒麟"); HeroNode heroNode3 = new HeroNode(3,"吴用","智多星"); HeroNode heroNode4 = new HeroNode(4,"林冲","豹子头"); // 创建单链表 SingleLinkedList singleLinkedList = new SingleLinkedList(); // 加入结点到单链表 singleLinkedList.addNode(heroNode); singleLinkedList.addNode(heroNode2); singleLinkedList.addNode(heroNode3); singleLinkedList.addNode(heroNode4); // 遍历显示链表 singleLinkedList.showList(); } }
完整代码:
public class SingleLinkedListTest { public static void main(String[] args) { // 先创建4个结点 HeroNode heroNode = new HeroNode(1,"宋江","及时雨"); HeroNode heroNode2 = new HeroNode(2,"卢俊义","玉麒麟"); HeroNode heroNode3 = new HeroNode(3,"吴用","智多星"); HeroNode heroNode4 = new HeroNode(4,"林冲","豹子头"); // 创建单链表 SingleLinkedList singleLinkedList = new SingleLinkedList(); // 加入结点到单链表 singleLinkedList.addNode(heroNode); singleLinkedList.addNode(heroNode2); singleLinkedList.addNode(heroNode3); singleLinkedList.addNode(heroNode4); // 遍历显示链表 singleLinkedList.showList(); } }// 创建单链表SingleLinkedList,用来管理英雄人物,即:创建单链表来操作结点. class SingleLinkedList { // 先初始化一个头结点head,头结点不要动,头结点不存放具体的数据,作用:用来连接整个链表,表示单链表的头 // 初始化HeroNode private HeroNode head = new HeroNode(0,"",""); // 编写成员方法addNode(HeroNode heroNode),实现添加结点到单向链表 public void addNode(HeroNode heroNode){ // 无返回值带参 // 因为head结点不能动,因此需要一个临时变量(或者叫做辅助指针) temp HeroNode temp = head; // 通过死循环遍历链表,找到链表中的最后一个结点 while(true){ if(temp.next == null){ // 找到链表的最后最后一个结点了,因为temp.next = null 了 break; } // 若没有找到最后一个结点就将temp后移,就将temp后移,继续找 temp = temp.next; } // 当退出while循环时,说明找到链表的最后一个结点,temp也指向了最后一个结点。 // 将最后一个结点的next域指向要添加的结点(即:新结点)即可 temp.next = heroNode; } // 编写成员方法showList() , 实现显示链表,通过遍历完成 public void showList() { // 判断链表是否为空 if(head.next == null) { // 说明链表为空 System.out.println("链表为空"); return; } // 1. 因为头结点head不能动,因此需要一个临时变量(或者叫做辅助指针)来遍历 // 链表不为空,说明链表中至少有一个数据 HeroNode temp = head.next; while(true){ // 2. 判断是否遍历到了链表的最后一个结点 if(temp == null) { // 说明已遍历到链表最后一个结点,temp = null,后面就没有结点可以遍历了 break; } // 3. 不为空(也就是还没有遍历到链表的最后一个结点),输入结点信息 System.out.println(temp); // 重写了toString方法的,会打印整个结点信息 // 4. 将temp后移,若不后移会造成死循环,一定记住。 temp = temp.next; } } }// 首先定义一个HeroNode类,每个HeroNode对象就是一个结点 class HeroNode { // 编写属性(共4个) // data域 用来存放具体的数据 public int id; public String name; public String nickName; // next域 用来指向下一个结点 public HeroNode next; // 最重要的一个属性 // 无参构造 public HeroNode() { } // 带参构造,实现将局部变量的值赋值给成员变量 public HeroNode(int id,String name,String nickName) { // 对属性进行初始化 this.id = id; this.name = name; this.nickName = nickName; } // 重写toString()方法,为了显示 | 遍历方便 @Override public String toString() { return "HeroNode{" + "id=" + id + ", name='" + name + '\'' + ", nickName='" + nickName + '\'' + '}'; } }
运行截图:
问题及优化:
- 目前
addNode(HeroNode heroNode)
方法没有考虑排序 - 要求:添加结点时,考虑排序
- 点赞
- 收藏
- 分享
- 文章举报
- 基于Python单向循环链表实现尾部、任意位置添加,删除
- 基于Python单向链表实现尾部、任意位置添加,删除
- (C语言版)链表(一)——实现单向链表创建、插入、删除等简单操作(包含个人理解说明及注释,新手跟着写代码)
- (C语言版)链表(一)——实现单向链表创建、插入、删除等简单操作(包含个人理解说明及注释,新手跟着写代码)
- 单向链表之创建添加(C语言实现)
- (C语言版)链表(一)——实现单向链表创建、插入、删除等简单操作(包含个人理解说明及注释,新手跟着写代码)
- 直接插入排序的C++实现代码
- 【自编小代码】用链表实现对键盘的输入进行排序
- java实现单向链表--创建、遍历
- 用链表实现直接选择排序和直接插入排序
- 排序技术_各种算法原理 图解 代码实现
- 经典排序 图解 代码实现
- Java编程实现直接插入排序代码示例
- 排序技术_各种算法原理 图解 代码实现
- 排序--直接插入排序思想及代码实现
- 单向链表的相关操作总结:创建、删除、查找、排序、统计链表大小、链表的反转和遍历等
- Android 添加背景音乐代码实现,以及创建音频文件夹
- 创建带表头结点的单向链表,并实现各种功能
- 数据结构:链表创建、显示、求和、插入、删除等操作的代码实现
- 实现单向循环链表的创建、测长、打印、插入、删除及逆置