1、链表(Linked List)介绍
1.1、内存结构
- 内存上来看:链表存储空间不连续(不像数组)
- 假设有一个线性表为(a1,a2,a3,a4,a5,a6),则其对应的带有头结点的链表表示及存储结构如图所示,其中head指针的值为2020H。
1.2 、逻辑结构
- 逻辑上来看:链表属于线性结构
- 含有n个元素的线性表通过每个结点的指针域链接成一个链表。又由于此链表的每个结点中只有一个指向后继的指针,所以称其为单链表或线性链表。下图的单链表是带有头结点的单链表,头结点的作用是方便单链表的特殊操作,简化代码。其中,头指针head指向链表的头结点,各元素结点的指针指向下一个结点,而最后一个结点的指针为“空”(NULL)。从头指针开始便可沿着链找到链表各个元素,因此可用头指针来表示一个单链表。
2、单链表的实现
2.1、水浒英雄榜
- 单链表的应用实例
- 使用带head头的单向链表实现 –水浒英雄排行榜管理
- 完成对英雄人物的增删改查操作, 注: 删除和修改,查找可以考虑学员独立完成,也可带学员完成
- 第一种方法在添加英雄时,直接添加到链表的尾部
- 第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
2.2、链表节点定义
- no :英雄编号
- name :英雄名字
- nickName :英雄昵称
- next :指向下一个 HeroNode 节点
//定义HeroNode,每个HeroNode对象就是一个节点
class HeroNode{
public int no; //英雄的编号
public String name; //英雄的名字
public String nickname; //英雄的称号
public HeroNode next; //指向下一节点
//构造器
public HeroNode(int no,String name,String nickname){
this.no = no;
this.name = name;
this.nickname = nickname;
}
//重写toString方法
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
2.3、链表定义
- Head :头结点不存放数据,仅仅作为当前链表的入口
- head 字段的值不能改变,一旦改变,就丢失了整个链表的入口,我们也就无法通过 head 找到链表了
/定义SingleLikedList管理英雄
class SingleLikedList{
//先初始化头结点,固定不要动,不存放数据
private HeroNode head = new HeroNode(0,"","");
2.4、遍历链表
- 定义辅助变量 temp ,相当于一个指针,指向当前节点
- 何时遍历完成?temp == null 表明当前节点为 null ,即表示已到链表末尾
- 如何遍历?temp = temp.next ,每次输出当前节点信息之后,temp 指针后移
//显示链表【遍历】
public void list(){
//链表先判空
if (head.next == null){
System.out.println("链表为空");
return;
}
//添加一个辅助的节点temp,因为head节点不能动
HeroNode temp = head.next;
while (true){
if (temp == null)break; //已经遍历完退出循环
System.out.println(temp); //输出链表信息
temp = temp.next; //继续遍历,temp后移一个节点
}
}
2.5、添加节点
- 代码思路
- 定义辅助变量 temp ,相当于一个指针,指向当前节点
- 如何在链表末尾插入节点?
- 首先需要遍历链表,找到链表最后一个节点,当 temp.next == null时,temp 节点指向链表最后一个节点
- 然后在 temp 节点之后插入节点即可:temp.next = heroNode
//添加节点到单向链表,第二种方式有顺序,根据排名添加
public void addByOrder(HeroNode heroNode){
//添加一个辅助的节点temp,因为head节点不能动
//找到temp是位于添加位置的前一个节点,否则添加不了
HeroNode temp = head;
boolean flag = false; //flag代表需要添加的节点是否存在,默认false
while (true){
if (temp.next == null)break; //已经遍历完退出循环
if (temp.next.no > heroNode.no){
//位置找到,就在temp后面插入,temp的下一个节点和要添加的节点比较。
break;
}else if (temp.next.no == heroNode.no){
flag = true; //代表需要添加的heroNode节点存在
break;
}
temp = temp.next; //继续遍历,temp后移一个节点
}
//遍历完链表后判断flag的值
if (flag){
//不能添加,节点已经存在
System.out.printf("准备插入的英雄编号%d已经存在,不能加入\n",heroNode