上一篇呢,我们学习了使用数组模拟队列,大家都知道数据结构分为线性结构和非线性结构,线性结构呢,又有两种不同的存储结构,分别是 顺序存储结构(数组) 和链式存储结构(链表) ,今天我们就来学习链式存储结构中的单链表。
顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的。 链式存储的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息。
经过分析我们知道了,插入和删除时元素的原因是在内存中他们是相邻的,插入时中间没有间隙所以需要移动位置空出空隙,删除时中间又有了空隙,需要移动位置填补空隙。知道原因后,我们就发现问题原来是因为是挨着的,那我们是不是可以让所有元素都直接有空隙是不是更好,只要让元素知道他的下一个元素在哪里就ok了。这样,我们可以在第一个元素时,就知道第二个元素的位置(内存地址),而找到它;在第二个元素时,再找到第三个元素的位置(内存地址)。这样所有的元素我们就都可以通过遍历而找到。我超,这不就是线性表的链式存储结构(链表)吗?下面我们就来介绍一下线性表的链式存储结构(链表)。
链表是有序的列表,但是它在内存中是存储如下(应该能看懂吧,图太难画了...)
通过观察可知:
- 链表是以节点的方式来存储,是链式存储。
- 每个节点包含 data 域, next 域:指向下一个节点。
- 如图:发现链表的各个节点不一定是连续存储。
- 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定。
单链表(带头结点) 逻辑结构示意图如下
下面我们来学习创建单链表,我们以英雄联盟英雄为例子来创建单链表。赋予四个属性。
分别是:编号、名字、昵称、指向下一个节点(没有的话则为空)。添加构造方法,重写toString方法。代码如下:
//定义HeroNode,每一个HeroNode对象就是一个节点
class HeroNode {
public int no;//编号
public String name;//名字
public String nickname;//昵称
public HeroNode next;//指向下一个节点 如果没有则为空
public HeroNode() {
}
public HeroNode(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
下面我们来添加英雄,我们使用两种办法来添加今天先演示第一种,直接加入链表后。(第二种根据编号顺序来添加)
思路:
1、先初始化一个head,head不动,不存放数据。(为什么要初始化head呢?因为整个链表的存取都要从head开始,head指向链表的第一个节点)
1、找到当前链表的最后节点。(遍历查找)
2、将最后节点的next指向新的节点
由于头结点不能动所以我们需要一个辅助变量tmep,temp就是一个随着遍历而动的一个变量。
heroNode temp=head;如下图所示:(每遍历一个数据temp就往下跳一下)
代码如下:
class SingleLinkedList {
//先初始化一个头结点,头结点不动,不存放数据
HeroNode head = new HeroNode();
/**
* 思路,当不考虑编号顺序时
* 1、找到当前链表的最后节点
* 2、将最后这个节点的next指向新的节点
*
* 第一种方式在添加英雄时,直接往后添加就ok
*/
//添加节点到单链表
public void add(HeroNode heroNode) {
//因为head节点不能动,因此我们需要一个辅助变量temp
HeroNode temp = head;
//遍历链表,找到最后一个节点
while (true) {
//找到链表的最后
if (temp.next == null) {
break;
}
//没找到 temp往后移
temp = temp.next;
}
//当退出循环时,temp就指向了链表的最后
//将最后这个节点的next指向新的节点
temp.next = heroNode;
}
}
但是这样添加的话你加入的是什么顺序输出的就是什么顺序,第二种添加可以按照编号顺序添加,,如果这个编号的已存在也会提示该节点已存在,不过由于时间有限单链表的按照编号顺序添加以及修改删除遍历明天再写。