3.1 链表(Linked List)介绍
- 链表是有序的列表,在逻辑结构上是有序的,但它在内存中的物理结构是乱序的
小结:
链表是以节点的方式来存储,属于链式存储
每个节点包含 data域:存储数据,next域:指向下一个节点
链表的各个节点不一定是连续存储
链表分为 带头结点的链表 和 不带头结点的链表,根据实际需求确定
3.2 单链表的应用实例
使用带head头的单向链表实现:
水浒英雄排行榜管理,完成对英雄的增删改查操作
步骤:
- 第一种方法在添加英雄时,直接添加到链表的尾部,思路如下:
-
添加(创建):
先创建一个head头结点,用来表示单链表的头
后面每添加一个节点,就直接加入到链表的最后
-
遍历:
通过一个辅助变量遍历整个链表,寻找链表尾部
- 第二种方法在添加英雄时,根据排名将英雄添加到指定位置(如果已经存在该英雄,则添加失败并给出提示),思路如下:
-
需要根据编号的顺序添加:
假设新的英雄(节点)为 heroNode
首先需要找到新添加的节点的位置,通过辅助变量temp遍历找到
遍历链表判断节点位置的条件:temp.next.no > heroNode.next,条件成立时 temp 节点就是新的节点的上一个节点
找到结点位置后 添加:
java heroNode.next = temp.next temp.next = heroNode
-
修改英雄(节点)的功能
思路:
先通过遍历,找到该节点
找到之后:
temp.name = heroNode.name; temp.nickname = heroNode.nickname;
-
删除英雄(节点)
思路:
先找到需要删除的节点的上一个节点 temp
删除节点:
java temp.next = temp.next.next;
被删除的节点,不会有其他的引用指向,会被垃圾回收机制回收
-
完整代码演示:
public class SingleLinkedListDemo { public static void main(String[] args) { //小试一把 //先创建节点 HeroNode heroNode1 = new HeroNode(1, "宋江", "及时雨"); HeroNode heroNode2 = new HeroNode(2, "卢俊义", "玉麒麟"); HeroNode heroNode3 = new HeroNode(3, "吴用", "智多星"); HeroNode heroNode4 = new HeroNode(4, "林冲", "豹子头"); //创建一个链表 SingleLinkedList singleLinkedList = new SingleLinkedList(); //第一种添加英雄的方式,不考虑排序 // singleLinkedList.add(heroNode1); // singleLinkedList.add(heroNode3); // singleLinkedList.add(heroNode4); // singleLinkedList.add(heroNode2); //第二种添加英雄的方式,按编号添加英雄 singleLinkedList.addByOrder(heroNode1); singleLinkedList.addByOrder(heroNode3); singleLinkedList.addByOrder(heroNode4); singleLinkedList.addByOrder(heroNode2); //添加相同编号的英雄,查看是否提示 singleLinkedList.addByOrder(heroNode2); //显示单链表 singleLinkedList.list(); //测试修改节点方法 HeroNode newheroNode4 = new HeroNode(4, "小冲冲", "豹子头林冲"); singleLinkedList.update(newheroNode4); System.out.println("展示修改后的链表:"); singleLinkedList.list(); //测试删除英雄(节点) singleLinkedList.delete(2); System.out.println("展示删除英雄后的链表:"); singleLinkedList.list(); } } /** * 定义SingleLinkedList 管理英雄 */ class SingleLinkedList{ /** * 先初始化一个头结点,头节点不动,不存放具体的数据 */ HeroNode head = new HeroNode(0,"",""); /** * 第一种添加英雄的方式;不考虑顺序 * 添加节点到单向链表: * 添加节点(英雄)的位置为链表的尾节点 * 思路:当不考虑编号顺序时 * 1.找到当前链表的最后节点 * 2.将最后这个节点的next 指向要添加的新的节点 */ public void add(HeroNode heroNode){ //由于头节点不能动,需要一个指针temp辅助遍历 HeroNode temp = head; //遍历链表,直到找到最后 while (true){ //如果节点指向的下一个为空,那么这就是链表最后 if (temp.next == null){ break; } //如果没有找到链表的尾节点,就继续向后找,temp后移 temp = temp.next; } //此处循环已经完成,temp为尾节点 //将尾节点的next指向要添加的新节点,则添加节点成功 temp.next = heroNode; } /** * 第二种添加英雄的方式: * 根据排名将英雄插入到指定位置(如果有这个排名则添加失败,并给出提示) */ public void addByOrder(HeroNode heroNode){ //由于头节点不能动,需要一个指针temp辅助遍历 HeroNode temp = head; //标志是否存在相同排名的英雄,默认为false boolean loop = false; while (true){ //判断temp是否在链表的最后 if (temp.next == null){ break; } //找到英雄该插入的位置,应该插入到temp的后面 if (temp.next.no > heroNode.no){ break; //找到与该英雄相同编号的英雄,loop设置为true }else if (temp.next.no == heroNode.no){ loop = true; break; } //temp后移,循环遍历 temp = temp.next; } //loop为true,说明编号存在 if (loop){ System.out.printf("存在相同排名的英雄,编号为%d\n",temp.next.no); return; }else { //找到位置 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.next; } } /** * 根据编号修改英雄(节点)信息,编号不变 */ public void update(HeroNode heroNode){ //由于头节点不能动,需要一个指针temp辅助遍历 HeroNode temp = head.next; //表示是否找到该节点 boolean flag = true; //循环遍历查找节点 while (true){ //判断是否找到最后 if (temp == null){ break; }else { if (temp.no == heroNode.no){ //找到节点 flag = true; break; } } //未找到,节点后移 temp = temp.next; } //根据flag判断是否找到节点 if (flag){ //找到节点 temp.name = heroNode.name; temp.nickname = heroNode.nickname; }else { //没有找到节点 System.out.println("未找到该节点"); } } /** * 根据编号删除英雄(节点)的方法 */ public void delete(int no){ //由于头节点不能动,需要一个指针temp辅助遍历 HeroNode temp = head; //表示是否找到该节点 boolean flag = true; //循环遍历查找节点 while (true){ //判断是否找到最后 if (temp == null){ break; }else { if (temp.next.no == no){ //找到节点 flag = true; break; } } //未找到,节点后移 temp = temp.next; } //根据flag判断是否找到节点 if (flag){ //找到节点 temp.next = temp.next.next; }else { //没有找到节点 System.out.println("未找到该英雄(节点)"); } } } /** * 定义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; } @Override public String toString() { return "HeroNode{" + "no=" + no + ", name='" + name + '\'' + ", nickname='" + nickname + '\'' + '}'; } }
展示结果: