-
链表是以节点的方式来存储,是链式存储
-
每个节点包含 data 域, next 域:指向下一个节点.
-
链表的各个节点不一定是连续存储.
-
链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定
单链表的创建示意图(添加) 显示单向链表的分析
-
head节点: 不存放具体的数据, 作用就是表示单链表头next
-
HeroNode 数据next
-
HeroNode数据next
-
…
-
添加: 先创建一个head头节点, 作用就是表示单链表的头,后面我们每添加一个节点, 就直接加入到链表的最后
-
遍历: 通过一个辅助遍历, 帮助遍历整个链表
应用实例
-
使用带head头的单向链表实现 –水浒英雄排行榜管理
-
完成对英雄人物的增删改查操作, 注: 删除和修改,查找可以考虑学员独立完成,也可带学员完成
-
第一种方法在添加英雄时,直接添加到链表的尾部
package DataStructures;
public class SingleLinkedList {
public static void main(String[] args) {
// 进行测试
// 先创建节点
HeroNode hero1 = new HeroNode (1,"宋宋", "江江");
HeroNode hero2 = new HeroNode (2,"文文", "雨雨");
HeroNode hero3 = new HeroNode (3,"哈哈", "嘿嘿");
HeroNode hero4 = new HeroNode (4,"依依", "落落");
// 创建要给链表
SingleLinkedListD singleLinkedListD = new SingleLinkedListD();
// 加入
singleLinkedListD.add(hero1);
singleLinkedListD.add(hero2);
singleLinkedListD.add(hero3);
singleLinkedListD.add(hero4);
// 显示
singleLinkedListD.list();
}
}
// 定义SingleLinked 管理我们的英雄
class SingleLinkedListD {
//先初始化一个头节点, 头节点不要动,
private HeroNode head = new HeroNode(0,"","");
public void add(HeroNode heroNode) {
//因为head节点不能动, 因此我们需要一个辅助变量temp
HeroNode temp = head;
// 遍历链表, 找到最后
while(true) {
// 找到链表的最后
if (temp.next == null) {
break;
}
// 如果没有找到最后,就将temp后移
temp = temp.next;
}
// 当退出while循环时, temp就指向了链表的最后
// 将最后这个节点的next 指向 新的节点
temp.next = heroNode;
}
//显示链表(遍历)
public void list() {
// 判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
// 因为头节点, 不能动 因此我们需要一个辅助变量来遍历
HeroNode temp = head.next;
while(true) {
// 判断是否到链表最后
if (temp == null) {
break;
}
// 输出节点信息
System.out.println(temp);
// 将temp后移, 一定小心
temp = temp.next;
}
}
}
// 定义HeroNode , 每个HeroNode 对象就是一个节点
class HeroNode {
public int no;
public String name;
public String nickname;
public HeroNode next; // 指向下一个节点
// 构造器
public HeroNode(int no, String hName, String hNickname) {
this.no = no;
this.name = hName;
this.nickname = hNickname;
}
// 为了显示方法, 我们重新toString
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname;
}
}
-
第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
package DataStructures;
public class SingleLinkedList {
public static void main(String[] args) {
// 进行测试
// 先创建节点
HeroNode hero1 = new HeroNode (1,"宋宋", "江江");
HeroNode hero2 = new HeroNode (2,"文文", "雨雨");
HeroNode hero3 = new HeroNode (3,"哈哈", "嘿嘿");
HeroNode hero4 = new HeroNode (4,"依依", "落落");
// 创建要给链表
SingleLinkedListD singleLinkedListD = new SingleLinkedListD();
// 加入
// singleLinkedListD.add(hero1);
// singleLinkedListD.add(hero2);
// singleLinkedListD.add(hero3);
// singleLinkedListD.add(hero4);
// 加入按照编号的顺序
singleLinkedListD.addByOrder(hero1);
singleLinkedListD.addByOrder(hero2);
singleLinkedListD.addByOrder(hero3);
singleLinkedListD.addByOrder(hero4);
singleLinkedListD.addByOrder(hero3);
// 显示
singleLinkedListD.list();
}
}
// 定义SingleLinked 管理我们的英雄
class SingleLinkedListD {
//先初始化一个头节点, 头节点不要动,
private HeroNode head = new HeroNode(0,"","");
public void add(HeroNode heroNode) {
//因为head节点不能动, 因此我们需要一个辅助变量temp
HeroNode temp = head;
// 遍历链表, 找到最后
while(true) {
// 找到链表的最后
if (temp.next == null) {
break;
}
// 如果没有找到最后,就将temp后移
temp = temp.next;
}
// 当退出while循环时, temp就指向了链表的最后
// 将最后这个节点的next 指向 新的节点
temp.next = heroNode;
}
// 第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
public void addByOrder (HeroNode heroNode) {
// 因为头节点不能动, 因此我们仍然通过一个辅助指针(变量) 来帮助找到添加的位置
//因为单链表, 因此我们找的temp 是位于添加位置的前一个节点, 否则插入不了
HeroNode temp = head;
boolean flag = false; // flag标志添加的编号是否存在, 默认为false
while(true) {
if (temp.next == null) { // 说明temp已经在链表的最后
break;//
}
if (temp.next.no > heroNode.no) { // 位置找到, 就在temp 的后面插入
break;
} else if (temp.next.no == heroNode.no) {
// 说明希望添加的heroNode的编号已然存在
flag = true; // 说明编号存在
break;
}
temp = temp.next; // 后移, 遍历当前链表
}
// 判断flag 的值
if (flag) { // 不能添加,说明编号存在
System.out.println("准备插入的英雄编号%d已经存在了, 不能加入\n" + heroNode.no);
} else {
// 插入到链表中 temp的后面
heroNode.next = temp.next;
temp.next = heroNode;
}
}
//显示链表(遍历)
public void list() {
// 判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
// 因为头节点, 不能动 因此我们需要一个辅助变量来遍历
HeroNode temp = head.next;
while(true) {
// 判断是否到链表最后
if (temp == null) {
break;
}
// 输出节点信息
System.out.println(temp);
// 将temp后移, 一定小心
temp = temp.next;
}
}
}
// 定义HeroNode , 每个HeroNode 对象就是一个节点
class HeroNode {
public int no;
public String name;
public String nickname;
public HeroNode next; // 指向下一个节点
// 构造器
public HeroNode(int no, String hName, String hNickname) {
this.no = no;
this.name = hName;
this.nickname = hNickname;
}
// 为了显示方法, 我们重新toString
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname;
}
}
修改链表
package DataStructures;
public class SingleLinkedList {
public static void main(String[] args) {
// 进行测试
// 先创建节点
HeroNode hero1 = new HeroNode (1,"宋宋", "江江");
HeroNode hero2 = new HeroNode (2,"文文", "雨雨");
HeroNode hero3 = new HeroNode (3,"哈哈", "嘿嘿");
HeroNode hero4 = new HeroNode (4,"依依", "落落");
// 创建要给链表
SingleLinkedListD singleLinkedListD = new SingleLinkedListD();
// 加入
// singleLinkedListD.add(hero1);
// singleLinkedListD.add(hero2);
// singleLinkedListD.add(hero3);
// singleLinkedListD.add(hero4);
// 加入按照编号的顺序
singleLinkedListD.addByOrder(hero1);
singleLinkedListD.addByOrder(hero2);
singleLinkedListD.addByOrder(hero3);
singleLinkedListD.addByOrder(hero4);
singleLinkedListD.addByOrder(hero3);
//显示
singleLinkedListD.list();
HeroNode newHeroNode = new HeroNode(2,"文","雨~~");
singleLinkedListD.update(newHeroNode);
System.out.println("修改后的链表情况");
singleLinkedListD.list();
}
}
// 定义SingleLinked 管理我们的英雄
class SingleLinkedListD {
//先初始化一个头节点, 头节点不要动,
private HeroNode head = new HeroNode(0,"","");
public void add(HeroNode heroNode) {
//因为head节点不能动, 因此我们需要一个辅助变量temp
HeroNode temp = head;
// 遍历链表, 找到最后
while(true) {
// 找到链表的最后
if (temp.next == null) {
break;
}
// 如果没有找到最后,就将temp后移
temp = temp.next;
}
// 当退出while循环时, temp就指向了链表的最后
// 将最后这个节点的next 指向 新的节点
temp.next = heroNode;
}
// 第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
public void addByOrder (HeroNode heroNode) {
// 因为头节点不能动, 因此我们仍然通过一个辅助指针(变量) 来帮助找到添加的位置
//因为单链表, 因此我们找的temp 是位于添加位置的前一个节点, 否则插入不了
HeroNode temp = head;
boolean flag = false; // flag标志添加的编号是否存在, 默认为false
while(true) {
if (temp.next == null) { // 说明temp已经在链表的最后
break;//
}
if (temp.next.no > heroNode.no) { // 位置找到, 就在temp 的后面插入
break;
} else if (temp.next.no == heroNode.no) {
// 说明希望添加的heroNode的编号已然存在
flag = true; // 说明编号存在
break;
}
temp = temp.next; // 后移, 遍历当前链表
}
// 判断flag 的值
if (flag) { // 不能添加,说明编号存在
System.out.println("准备插入的英雄编号%d已经存在了, 不能加入\n" + heroNode.no);
} else {
// 插入到链表中 temp的后面
heroNode.next = temp.next;
temp.next = heroNode;
}
}
// 修改节点的信息, 根据no的编号来修改, 即no编号不能改
//说明
// 1. 根据newHeroNode 的no来修改即可
public void update(HeroNode newHeroNode) {
// 判断是否空
if (head.next == null) {
System.out.println("链表为空");
return;
}
// 根据需要修改的节点, 根据no编写
// 定义一个辅助变量
HeroNode temp = head.next;
boolean flag = false; // 表示是否找到该节点
while(true) {
if (temp == null) {
break;// 已经遍历完链表
}
if (temp.no == newHeroNode.no) {
//找到
flag = true;
break;
}
temp = temp.next;
}
//根据flag 判断是否找到要修改的节点
if (flag) {
temp.name = newHeroNode.name;
temp.nickname = newHeroNode.nickname;
} else { // 没有找到
System.out.println("没有找到编号为%d 的节点, 不能修改\n" + newHeroNode.no);
}
}
//显示链表(遍历)
public void list() {
// 判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
// 因为头节点, 不能动 因此我们需要一个辅助变量来遍历
HeroNode temp = head.next;
while(true) {
// 判断是否到链表最后
if (temp == null) {
break;
}
// 输出节点信息
System.out.println(temp);
// 将temp后移, 一定小心
temp = temp.next;
}
}
}
// 定义HeroNode , 每个HeroNode 对象就是一个节点
class HeroNode {
public int no;
public String name;
public String nickname;
public HeroNode next; // 指向下一个节点
// 构造器
public HeroNode(int no, String hName, String hNickname) {
this.no = no;
this.name = hName;
this.nickname = hNickname;
}
// 为了显示方法, 我们重新toString
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname;
}
}
删除链表
从单链表中删除一个节点的思路
-
我们先找到需要删除的这个节点的前一个节点temp
-
temp.next = temp.next.next;
-
被删除的节点,将不会有其他引用指向, 会被垃圾回收机制回收
package DataStructures;
public class SingleLinkedList {
public static void main(String[] args) {
// 进行测试
// 先创建节点
HeroNode hero1 = new HeroNode (1,"宋宋", "江江");
HeroNode hero2 = new HeroNode (2,"文文", "雨雨");
HeroNode hero3 = new HeroNode (3,"哈哈", "嘿嘿");
HeroNode hero4 = new HeroNode (4,"依依", "落落");
// 创建要给链表
SingleLinkedListD singleLinkedListD = new SingleLinkedListD();
// 加入
// singleLinkedListD.add(hero1);
// singleLinkedListD.add(hero2);
// singleLinkedListD.add(hero3);
// singleLinkedListD.add(hero4);
// 加入按照编号的顺序
singleLinkedListD.addByOrder(hero1);
singleLinkedListD.addByOrder(hero2);
singleLinkedListD.addByOrder(hero3);
singleLinkedListD.addByOrder(hero4);
singleLinkedListD.addByOrder(hero3);
//显示
singleLinkedListD.list();
HeroNode newHeroNode = new HeroNode(2,"文","雨~~");
singleLinkedListD.update(newHeroNode);
System.out.println("修改后的链表情况");
singleLinkedListD.list();
// 删除一个节点
singleLinkedListD.del(1);
System.out.println("删除后的链表情况");
singleLinkedListD.list();
}
}
// 定义SingleLinked 管理我们的英雄
class SingleLinkedListD {
//先初始化一个头节点, 头节点不要动,
private HeroNode head = new HeroNode(0,"","");
public void add(HeroNode heroNode) {
//因为head节点不能动, 因此我们需要一个辅助变量temp
HeroNode temp = head;
// 遍历链表, 找到最后
while(true) {
// 找到链表的最后
if (temp.next == null) {
break;
}
// 如果没有找到最后,就将temp后移
temp = temp.next;
}
// 当退出while循环时, temp就指向了链表的最后
// 将最后这个节点的next 指向 新的节点
temp.next = heroNode;
}
// 第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
public void addByOrder (HeroNode heroNode) {
// 因为头节点不能动, 因此我们仍然通过一个辅助指针(变量) 来帮助找到添加的位置
//因为单链表, 因此我们找的temp 是位于添加位置的前一个节点, 否则插入不了
HeroNode temp = head;
boolean flag = false; // flag标志添加的编号是否存在, 默认为false
while(true) {
if (temp.next == null) { // 说明temp已经在链表的最后
break;//
}
if (temp.next.no > heroNode.no) { // 位置找到, 就在temp 的后面插入
break;
} else if (temp.next.no == heroNode.no) {
// 说明希望添加的heroNode的编号已然存在
flag = true; // 说明编号存在
break;
}
temp = temp.next; // 后移, 遍历当前链表
}
// 判断flag 的值
if (flag) { // 不能添加,说明编号存在
System.out.println("准备插入的英雄编号%d已经存在了, 不能加入\n" + heroNode.no);
} else {
// 插入到链表中 temp的后面
heroNode.next = temp.next;
temp.next = heroNode;
}
}
// 修改节点的信息, 根据no的编号来修改, 即no编号不能改
//说明
// 1. 根据newHeroNode 的no来修改即可
public void update(HeroNode newHeroNode) {
// 判断是否空
if (head.next == null) {
System.out.println("链表为空");
return;
}
// 根据需要修改的节点, 根据no编写
// 定义一个辅助变量
HeroNode temp = head.next;
boolean flag = false; // 表示是否找到该节点
while(true) {
if (temp == null) {
break;// 已经遍历完链表
}
if (temp.no == newHeroNode.no) {
//找到
flag = true;
break;
}
temp = temp.next;
}
//根据flag 判断是否找到要修改的节点
if (flag) {
temp.name = newHeroNode.name;
temp.nickname = newHeroNode.nickname;
} else { // 没有找到
System.out.println("没有找到编号为%d 的节点, 不能修改\n" + newHeroNode.no);
}
}
//删除节点
//思路
//1.head不能动 因此我们需要temp辅助节点找到待删除节点的前一个节点
// 2. 说明我们在比较时,时temp.next.no和需要删除的节点的no比较
public void del(int no) {
HeroNode temp = head;
boolean flag = false; // 标志是否找到待删除节点的
while (true) {
if (temp.next == null) { //已经到链表的最后
break;
}
if (temp.next.no == no) {
// 找到了待删除的前一个节点temp
flag = true;
break;
}
temp = temp.next;// temp后移 遍历
}
//判断flag
if (flag) { // 找到
// 可以删除
temp.next = temp.next.next;
}else {
System.out.println("要删除的%d 节点不存在\n" + no);
}
}
//显示链表(遍历)
public void list() {
// 判断链表是否为空
if (head.next == null) {
System.out.println("链表为空");
return;
}
// 因为头节点, 不能动 因此我们需要一个辅助变量来遍历
HeroNode temp = head.next;
while(true) {
// 判断是否到链表最后
if (temp == null) {
break;
}
// 输出节点信息
System.out.println(temp);
// 将temp后移, 一定小心
temp = temp.next;
}
}
}
// 定义HeroNode , 每个HeroNode 对象就是一个节点
class HeroNode {
public int no;
public String name;
public String nickname;
public HeroNode next; // 指向下一个节点
// 构造器
public HeroNode(int no, String hName, String hNickname) {
this.no = no;
this.name = hName;
this.nickname = hNickname;
}
// 为了显示方法, 我们重新toString
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname;
}
}