4.1链表(Linked List)介绍
链表是有序的列表,但是它在内存中是存储如下
小结:
- 链表是以节点的方式来存储,是链式存储
- 每个节点包含 data 域, next 域:指向下一个节点.
- 如图:发现链表的各个节点不一定是连续存储.
- 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定
单链表(带头结点) 逻辑结构示意图如下
4.2单链表的应用实例
使用带head头的单向链表实现 –水浒英雄排行榜管理
完成对英雄人物的增删改查操作, 注: 删除和修改,查找可以考虑学员独立完成,也可带学员完成
1) 第一种方法在添加英雄时,直接添加到链表的尾部
思路分析示意图:
2) 第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)
3) 修改节点功能
思路
(1) 先找到该节点,通过遍历,
(2) tempname = newHeroNode.name ;
temp.nickname= newHeroNode.nickname
4) 删除节点
思路分析的示意图:
5.代码演示
1-单链表创建和遍历的分析实现
/*** 单链表*/
public class SingleLinkedListDemo {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, "林冲", "豹子头");//创建链表SingleLinkedList linkedList = new SingleLinkedList();linkedList.add(hero1);linkedList.add(hero2);linkedList.add(hero3);linkedList.add(hero4);//显示linkedList.list();}
}//定义SingleLinkedList 管理我们的英雄
class SingleLinkedList {//先初始化一个头节点,头节点不要动,不存放具体的数据private HeroNode head = new HeroNode(0, "", "");//添加节点到单向链表//思路:当不考虑编号顺序时//1.找到当前链表的最后节点//2.将最后这个节点的next 指向 新的节点public void add(HeroNode heroNode) {//因为head节点不能动,因此我们需要一个辅助遍历 tempHeroNode 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 {private int no;private String name;private String nickName;public HeroNode next;//指向下一个节点//构造器public HeroNode(int no, String name, String nickName) {this.no = no;this.name = name;this.nickName = nickName;}//为了显示方法,重新toString方法@Overridepublic String toString() {return "HeroNode{" +"no=" + no +", name='" + name + '\'' +", nickName='" + nickName + '\'' +'}';//删除next打印的原因是因为,节点的下一个节点,有连着节点,所以删除/*return "HeroNode{" +"no=" + no +", name='" + name + '\'' +", nickName='" + nickName + '\'' +", next=" + next +'}';*/}
}
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
2-单链表按顺序插入节点
/*** 单链表*/
public class SingleLinkedListDemo {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, "林冲", "豹子头");//创建链表SingleLinkedList linkedList = new SingleLinkedList();linkedList.addByOrder(hero2);linkedList.addByOrder(hero3);linkedList.addByOrder(hero1);linkedList.addByOrder(hero4);//显示linkedList.list();}
}//定义SingleLinkedList 管理我们的英雄
class SingleLinkedList {//先初始化一个头节点,头节点不要动,不存放具体的数据private HeroNode head = new HeroNode(0, "", "");//第二种方式在添加英雄时,根据排名将英雄插入到指定位置// (如果有这个排名,则添加失败,并给出提示)public void addByOrder(HeroNode heroNode){//因为头节点不能动,因此我们仍然通过一个辅助指针(变量)来帮助找到添加的位置//因为单链表,因为我们找的temp 是位于 添加位置的前一个节点,否则插入不了HeroNode temp = head;boolean flag= false;//flag,标识添加的编号是否存在,默认为falsewhile (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.printf("准备插入的英雄的编号 %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 name, String nickName) {this.no = no;this.name = name;this.nickName = nickName;}//为了显示方法,重新toString方法@Overridepublic String toString() {return "HeroNode{" +"no=" + no +", name='" + name + '\'' +", nickName='" + nickName + '\'' +'}';}
}
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
3-单链表节点的修改
/*** 单链表*/
public class SingleLinkedListDemo {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, "林冲", "豹子头");//创建链表SingleLinkedList linkedList = new SingleLinkedList();//链表排序linkedList.addByOrder(hero2);linkedList.addByOrder(hero3);linkedList.addByOrder(hero1);linkedList.addByOrder(hero4);//显示linkedList.list();System.out.println("\n修改后的链表:");HeroNode updhero2 = new HeroNode(2, "小卢", "小旺旺");linkedList.update(updhero2);//显示linkedList.list();}
}//定义SingleLinkedList 管理我们的英雄
class SingleLinkedList {//先初始化一个头节点,头节点不要动,不存放具体的数据private HeroNode head = new HeroNode(0, "", "");//添加节点到单向链表//思路:当不考虑编号顺序时//1.找到当前链表的最后节点//2.将最后这个节点的next 指向 新的节点public void add(HeroNode heroNode) {//因为head节点不能动,因此我们需要一个辅助遍历 tempHeroNode 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,标识添加的编号是否存在,默认为falsewhile (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.printf("准备插入的英雄的编号 %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.printf("没有找到 编号 %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 name, String nickName) {this.no = no;this.name = name;this.nickName = nickName;}//为了显示方法,重新toString方法@Overridepublic String toString() {return "HeroNode{" +"no=" + no +", name='" + name + '\'' +", nickName='" + nickName + '\'' +'}';}
}
HeroNode{no=1, name='宋江', nickName='及时雨'}HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}修改后的链表:
HeroNode{no=1, name='宋江', nickName='及时雨'}HeroNode{no=2, name='小卢', nickName='小旺旺'}HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}
4-单链表节点的删除和小计
/*** 单链表*/
public class SingleLinkedListDemo {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, "林冲", "豹子头");//创建链表SingleLinkedList linkedList = new SingleLinkedList();//链表排序linkedList.addByOrder(hero2);linkedList.addByOrder(hero3);linkedList.addByOrder(hero1);linkedList.addByOrder(hero4);//显示linkedList.list();System.out.println("\n删除后的节点:");linkedList.del(1);linkedList.list();}
}//定义SingleLinkedList 管理我们的英雄
class SingleLinkedList {//先初始化一个头节点,头节点不要动,不存放具体的数据private HeroNode head = new HeroNode(0, "", "");//添加节点到单向链表//思路:当不考虑编号顺序时//1.找到当前链表的最后节点//2.将最后这个节点的next 指向 新的节点public void add(HeroNode heroNode) {//因为head节点不能动,因此我们需要一个辅助遍历 tempHeroNode 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,标识添加的编号是否存在,默认为falsewhile (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.printf("准备插入的英雄的编号 %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.printf("没有找到 编号 %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) {//找到的待删除节点的前一个节点 tempflag = true;break;}temp = temp.next;//temp后移,遍历}//判断flagif (flag) {//找到,可以删除temp.next = temp.next.next;} else {System.out.printf("要删除的 %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 name, String nickName) {this.no = no;this.name = name;this.nickName = nickName;}//为了显示方法,重新toString方法@Overridepublic String toString() {return "HeroNode{" +"no=" + no +", name='" + name + '\'' +", nickName='" + nickName + '\'' +'}';}
}
HeroNode{no=1, name='宋江', nickName='及时雨'}
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}删除后的节点:
HeroNode{no=2, name='卢俊义', nickName='玉麒麟'}
HeroNode{no=3, name='吴用', nickName='智多星'}
HeroNode{no=4, name='林冲', nickName='豹子头'}