【数据结构(三)】单链表(1)

文章目录

  • 1. 链表介绍
  • 2. 单链表应用实例
    • 2.1. 顺序添加方式
      • 2.1.1. 思路分析
      • 2.1.2. 代码实现
    • 2.2. 按照编号顺序添加方式
      • 2.2.1. 思路分析
      • 2.2.2. 代码实现
  • 3. 单链表节点的修改
    • 3.1. 思路分析
    • 3.2. 代码实现
  • 4. 单链表节点的删除
    • 4.1. 思路分析
    • 4.2. 代码实现
  • 5. 单链表常见面试题
    • 5.1. 求单链表中有效节点的个数
    • 5.2. 查找单链表中倒数第k个节点
    • 5.3. 单链表的反转
    • 5.4. 从尾到头打印单链表


摘要:在Java中,可以使用类来实现链表结构,每个节点作为类的实例,包含数据和指向下一个节点的引用(以及可能的前一个节点的引用,对于双向链表)。通过操作节点的引用,可以实现链表的各种操作,如插入、删除、查找等。


1. 链表介绍

相关概念

    链表是一种常见的数据结构,它由一系列节点组成,每个节点包含数据部分和指向下一个节点的引用。链表有多种类型,包括单向链表、双向链表和循环链表。

单向链表(Singly Linked List):每个节点包含数据和指向下一个节点的引用。
双向链表(Doubly Linked List):每个节点包含数据、指向前一个节点的引用和指向下一个节点的引用。
循环链表(Circular Linked List):尾节点指向头节点,形成一个环形结构。

    链表的优点之一是可以动态地分配内存空间,而数组在创建时需要确定大小。然而,链表的缺点包括不能随机访问元素,需要从头开始逐个遍历,而且需要额外的空间来存储指针。


链表是有序的列表,但是它在内存中是存储如下:

在这里插入图片描述

小结上图:

  1. 链表是以节点的方式来存储,是链式存储
  2. 每个节点包含 data域next域:指向下一个节点;
  3. 如图:发现链表的各个节点不一定是连续存储;
        

链表分带头节点的链表没有头节点的链表,根据实际的需求来确定。

单链表(带头结点) 逻辑结构示意图如下:

在这里插入图片描述

2. 单链表应用实例

使用带 head 头的单向链表实现 –水浒英雄排行榜管理,完成对英雄人物的操作。有两种方式实现,解释如下:

给链表中的数据加入编号属性,加入需要添加4个链表数据,其编号分别为1、2、3、4。
    ①按照添加顺序(顺序添加方式):根据添加的顺序依次加入到链表中(如:添加顺序是1、4、2、3,在链表中的顺序也是1、4、2、3。)
    ②按照编号顺序(编号顺序添加方式):对加入链表中数据进行编号,根据编号进行排序(如:添加的顺序是1、4、2、3,而在链表中会按照1、2、3、4编号顺序)

2.1. 顺序添加方式

2.1.1. 思路分析

第一种方法在添加英雄时,直接添加到链表的尾部

思路分析示意图:

在这里插入图片描述

2.1.2. 代码实现

按照加入的先后顺序形成链表

package linkedlist;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 singleLinkedList = new SingleLinkedList();//加入:按照加入的先后顺序形成链表singleLinkedList.add(hero1);singleLinkedList.add(hero2);singleLinkedList.add(hero3);singleLinkedList.add(hero4);//显示singleLinkedList.list();}
}//定义SingleLinkedList 管理我们的英雄人物
class SingleLinkedList{//先初始化一个头节点,头节点不要动,不存放具体的数据private HeroNode head = new HeroNode(0, null, null);//添加节点到单向链表//思路:当不考虑编号顺序时//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对象就是一个节点HeroNode temp = head.next;while (true) {//判断是否到链表最后if(temp == null){break;}//输出节点的信息System.out.println(temp);//将next后移。(不后移就成了死循环,一定小心)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 + ", next = " + next + "]";return "HeroNode [no = " + no + ", name = " + name + ", nickname = " + nickname + "]";}}

运行结果:

在这里插入图片描述

2.2. 按照编号顺序添加方式

2.2.1. 思路分析

第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)

思路的分析示意图:

在这里插入图片描述

按照编号的顺序添加

  1. 首先找到新添加的节点的位置, 是通过辅助变量(指针), 通过遍历来搞定
  2. 新的节点.next = temp.next
  3. temp.next = 新的节点

2.2.2. 代码实现

package linkedlist;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 singleLinkedList = new SingleLinkedList();//加入// singleLinkedList.add(hero1);// singleLinkedList.add(hero4);// singleLinkedList.add(hero2);// singleLinkedList.add(hero3);//加入按照编号的顺序singleLinkedList.addByOrder(hero1);singleLinkedList.addByOrder(hero4);singleLinkedList.addByOrder(hero2);singleLinkedList.addByOrder(hero3);singleLinkedList.addByOrder(hero3);//显示singleLinkedList.list();}
}//定义SingleLinkedList 管理我们的英雄人物
class SingleLinkedList{//先初始化一个头节点,头节点不要动,不存放具体的数据private HeroNode head = new HeroNode(0, null, null);//添加节点到单向链表//思路:当不考虑编号顺序时//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;//标识添加的编号是否已经存在,默认为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对象就是一个节点HeroNode temp = head.next;while (true) {//判断是否到链表最后if(temp == null){break;}//输出节点的信息System.out.println(temp);//将next后移。(不后移就成了死循环,一定小心)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 + ", next = " + next + "]";return "HeroNode [no = " + no + ", name = " + name + ", nickname = " + nickname + "]";}
}

上述代码,添加的顺序是按照节点1、4、2、3、3的顺序添加,最后实现的结果是按照编号的顺序添加,并且如果重复添加会给出重复信息。

运行结果:

在这里插入图片描述

3. 单链表节点的修改

3.1. 思路分析

思路
(1) 先找到该节点,通过遍历
(2) temp.name = newHeroNode.name ; temp.nickname= newHeroNode.nicknam

3.2. 代码实现

package Linkedlist;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 singleLinkedList = new SingleLinkedList();//加入// singleLinkedList.add(hero1);// singleLinkedList.add(hero4);// singleLinkedList.add(hero2);// singleLinkedList.add(hero3);//加入按照编号的顺序singleLinkedList.addByOrder(hero1);singleLinkedList.addByOrder(hero4);singleLinkedList.addByOrder(hero2);singleLinkedList.addByOrder(hero3);// singleLinkedList.addByOrder(hero3);//显示一把singleLinkedList.list();//测试修改节点的代码HeroNode newHeroNode = new HeroNode(2, "小卢", "玉麒麟~~");singleLinkedList.update(newHeroNode);System.out.println("修改后的链表情况~~");singleLinkedList.list();}
}//定义SingleLinkedList 管理我们的英雄人物
class SingleLinkedList{//先初始化一个头节点,头节点不要动,不存放具体的数据private HeroNode head = new HeroNode(0, null, null);//添加节点到单向链表//思路:当不考虑编号顺序时//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;//标识添加的编号是否已经存在,默认为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对象就是一个节点HeroNode temp = head.next;while (true) {//判断是否到链表最后if(temp == null){break;}//输出节点的信息System.out.println(temp);//将next后移。(不后移就成了死循环,一定小心)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 + ", next = " + next + "]";return "HeroNode [no = " + no + ", name = " + name + ", nickname = " + nickname + "]";}
}

运行结果:

在这里插入图片描述

4. 单链表节点的删除

4.1. 思路分析

思路分析示意图:

在这里插入图片描述

从单链表中删除一个节点的思路

  1. 先找到 需要删除的这个节点的前一个节点 temp
  2. temp.next = temp.next.next
  3. 被删除的节点,将不会有其它引用指向,会被垃圾回收机制回收

4.2. 代码实现

package Linkedlist;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 singleLinkedList = new SingleLinkedList();//加入// singleLinkedList.add(hero1);// singleLinkedList.add(hero4);// singleLinkedList.add(hero2);// singleLinkedList.add(hero3);//加入按照编号的顺序singleLinkedList.addByOrder(hero1);singleLinkedList.addByOrder(hero4);singleLinkedList.addByOrder(hero2);singleLinkedList.addByOrder(hero3);// singleLinkedList.addByOrder(hero3);//显示一把singleLinkedList.list();//测试修改节点的代码HeroNode newHeroNode = new HeroNode(2, "小卢", "玉麒麟~~");singleLinkedList.update(newHeroNode);System.out.println("修改后的链表情况~~");singleLinkedList.list();//删除一个节点System.out.println("删除后的链表情况~~");singleLinkedList.del(1);singleLinkedList.del(4);singleLinkedList.del(2);singleLinkedList.del(3);singleLinkedList.list();}
}//定义SingleLinkedList 管理我们的英雄人物
class SingleLinkedList{//先初始化一个头节点,头节点不要动,不存放具体的数据private HeroNode head = new HeroNode(0, null, null);//添加节点到单向链表//思路:当不考虑编号顺序时//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;//标识添加的编号是否已经存在,默认为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对象就是一个节点HeroNode temp = head.next;while (true) {//判断是否到链表最后if(temp == null){break;}//输出节点的信息System.out.println(temp);//将next后移。(不后移就成了死循环,一定小心)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 + ", next = " + next + "]";return "HeroNode [no = " + no + ", name = " + name + ", nickname = " + nickname + "]";}
}

运行结果:
在这里插入图片描述

5. 单链表常见面试题

5.1. 求单链表中有效节点的个数

功能实现:

//方法:获取单链表的节点个数(如果是带头节点的链表,那么久不统计头节点)/*** * @param head* @return*/public static int getLength(HeroNode head){if(head.next == null){//空链表return 0;}int length = 0;//定义一个辅助变量,没有统计头节点HeroNode cur = head.next;while(cur != null) {length++;cur = cur.next;//遍历}return length;}

完整代码实现:

package Linkedlist;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 singleLinkedList = new SingleLinkedList();//加入按照编号的顺序singleLinkedList.addByOrder(hero1);singleLinkedList.addByOrder(hero4);singleLinkedList.addByOrder(hero2);singleLinkedList.addByOrder(hero3);// singleLinkedList.addByOrder(hero3);//显示一把singleLinkedList.list();//测试修改节点的代码HeroNode newHeroNode = new HeroNode(2, "小卢", "玉麒麟~~");singleLinkedList.update(newHeroNode);System.out.println("修改后的链表情况~~");singleLinkedList.list();//删除一个节点System.out.println("删除后的链表情况~~");singleLinkedList.del(1);singleLinkedList.del(4);
//        singleLinkedList.del(2);
//        singleLinkedList.del(3);singleLinkedList.list();//求单链表中有效节点个数System.out.println("有效的节点个数 = " + getLength(singleLinkedList.getHead()));}//方法:获取单链表的节点个数(如果是带头节点的链表,那么久不统计头节点)/*** * @param head* @return*/public static int getLength(HeroNode head){if(head.next == null){//空链表return 0;}int length = 0;//定义一个辅助变量,没有统计头节点HeroNode cur = head.next;while(cur != null) {length++;cur = cur.next;//遍历}return length;}
}//定义SingleLinkedList 管理我们的英雄人物
class SingleLinkedList{//先初始化一个头节点,头节点不要动,不存放具体的数据private HeroNode head = new HeroNode(0, null, null);//返回头节点public HeroNode getHead() {return head;}//添加节点到单向链表//思路:当不考虑编号顺序时//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;//标识添加的编号是否已经存在,默认为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对象就是一个节点HeroNode temp = head.next;while (true) {//判断是否到链表最后if(temp == null){break;}//输出节点的信息System.out.println(temp);//将next后移。(不后移就成了死循环,一定小心)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 + ", next = " + next + "]";return "HeroNode [no = " + no + ", name = " + name + ", nickname = " + nickname + "]";}
}

运行结果:

在这里插入图片描述

5.2. 查找单链表中倒数第k个节点

(新浪面试题)

在这里插入图片描述

功能实现:

    //查找单链表中的倒数第k个节点【新浪面试题】//思路//1. 编写一个方法,接收head节点,同时接收一个index//2. index 表示是倒数第index个节点//3. 先把链表从头到尾遍历,得到链表的总长度getLength//4. 得到size(getLength)后,我们从链表的第一个开始遍历(size-index)个,就可以得到//5. 如果找到了,则返回该节点,否则返回nullpublic static HeroNode findLastIndexNode(HeroNode head, int index){//判断如果链表为空,返回nullif(head.next == null){return null;//没有找到}//第一次遍历得到链表的长度(节点个数)int size = getLength(head);//第二次遍历 size-index 位置//先做一个index的校验if(index <= 0 || index > size){return null;}//定义一个辅助变量,for循环定位到倒数的indexHeroNode cur = head.next;//假设链表有3个数据,查找倒数第一个数据,那么size-index=3-1=2for(int i = 0; i < size - index; i++){cur = cur.next;}return cur;}

完整代码实现:

package Linkedlist;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 singleLinkedList = new SingleLinkedList();//加入按照编号的顺序singleLinkedList.addByOrder(hero1);singleLinkedList.addByOrder(hero4);singleLinkedList.addByOrder(hero2);singleLinkedList.addByOrder(hero3);// singleLinkedList.addByOrder(hero3);//显示链表singleLinkedList.list();System.out.println("--------------------------------");//查找倒数第k个节点HeroNode result = findLastIndexNode(singleLinkedList.getHead(), 1);System.out.println("result=" + result);}//查找单链表中的倒数第k个节点【新浪面试题】//思路//1. 编写一个方法,接收head节点,同时接收一个index//2. index 表示是倒数第index个节点//3. 先把链表从头到尾遍历,得到链表的总长度getLength//4. 得到size(getLength)后,我们从链表的第一个开始遍历(size-index)个,就可以得到//5. 如果找到了,则返回该节点,否则返回nullpublic static HeroNode findLastIndexNode(HeroNode head, int index){//判断如果链表为空,返回nullif(head.next == null){return null;//没有找到}//第一次遍历得到链表的长度(节点个数)int size = getLength(head);//第二次遍历 size-index 位置//先做一个index的校验if(index <= 0 || index > size){return null;}//定义一个辅助变量,for循环定位到倒数的indexHeroNode cur = head.next;//假设链表有3个数据,查找倒数第一个数据,那么size-index=3-1=2for(int i = 0; i < size - index; i++){cur = cur.next;}return cur;}//方法:获取单链表的节点个数(如果是带头节点的链表,那么久不统计头节点)/*** * @param head* @return*/public static int getLength(HeroNode head){if(head.next == null){//空链表return 0;}int length = 0;//定义一个辅助变量,没有统计头节点HeroNode cur = head.next;while(cur != null) {length++;cur = cur.next;//遍历}return length;}
}//定义SingleLinkedList 管理我们的英雄人物
class SingleLinkedList{//先初始化一个头节点,头节点不要动,不存放具体的数据private HeroNode head = new HeroNode(0, null, null);//返回头节点public HeroNode getHead() {return head;}//添加节点到单向链表//思路:当不考虑编号顺序时//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;//标识添加的编号是否已经存在,默认为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对象就是一个节点HeroNode temp = head.next;while (true) {//判断是否到链表最后if(temp == null){break;}//输出节点的信息System.out.println(temp);//将next后移。(不后移就成了死循环,一定小心)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 + ", next = " + next + "]";return "HeroNode [no = " + no + ", name = " + name + ", nickname = " + nickname + "]";}
}

运行结果:

在这里插入图片描述

5.3. 单链表的反转

(腾讯面试题)
在这里插入图片描述

分析思路:
    将原来链表中的第一个节点放到新链表的第一个位置(节点head后面),然后将原来第二个节点放到新链表的第一个位置(这样图中的数据5就在数据2前面了),再将数据5的next指向数据2。同理插入数据9。最后形成的新链表就是原链表反转的结果。

功能实现:

    //将单链表反转public static void reversetList(HeroNode head){//如果当前链表为空,或者只有一个节点,无需反转,直接返回if(head.next == null || head.next.next == null){return;}//定义一个辅助的指针(变量),帮助我们遍历原来的链表HeroNode cur = head.next;HeroNode next = null;//指向当前节点[cur]的下一个节点HeroNode reverseHead = new HeroNode(0, "", "");//遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead 的最前端while (cur != null) {next = cur.next;//先暂时保存当前节点的下一个节点,后面需要使用cur.next = reverseHead.next;//将cur的下一个节点指向新的链表的最前端reverseHead.next = cur;cur = next;//让cur后移}//将head.next 指向reverseHead.next,实现单链表的反转head.next = reverseHead.next;}

完整代码实现:

package Linkedlist;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 singleLinkedList = new SingleLinkedList();//加入// singleLinkedList.add(hero1);// singleLinkedList.add(hero4);// singleLinkedList.add(hero2);// singleLinkedList.add(hero3);//加入按照编号的顺序singleLinkedList.addByOrder(hero1);singleLinkedList.addByOrder(hero4);singleLinkedList.addByOrder(hero2);singleLinkedList.addByOrder(hero3);// singleLinkedList.addByOrder(hero3);//显示链表singleLinkedList.list();System.out.println("--------------------------------");reversetList(singleLinkedList.getHead());singleLinkedList.list();}//将单链表反转public static void reversetList(HeroNode head){//如果当前链表为空,或者只有一个节点,无需反转,直接返回if(head.next == null || head.next.next == null){return;}//定义一个辅助的指针(变量),帮助我们遍历原来的链表HeroNode cur = head.next;HeroNode next = null;//指向当前节点[cur]的下一个节点HeroNode reverseHead = new HeroNode(0, "", "");//遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead 的最前端while (cur != null) {next = cur.next;//先暂时保存当前节点的下一个节点,后面需要使用cur.next = reverseHead.next;//将cur的下一个节点指向新的链表的最前端reverseHead.next = cur;cur = next;//让cur后移}//将head.next 指向reverseHead.next,实现单链表的反转head.next = reverseHead.next;}//查找单链表中的倒数第k个节点【新浪面试题】//思路//1. 编写一个方法,接收head节点,同时接收一个index//2. index 表示是倒数第index个节点//3. 先把链表从头到尾遍历,得到链表的总长度getLength//4. 得到size(getLength)后,我们从链表的第一个开始遍历(size-index)个,就可以得到//5. 如果找到了,则返回该节点,否则返回nullpublic static HeroNode findLastIndexNode(HeroNode head, int index){//判断如果链表为空,返回nullif(head.next == null){return null;//没有找到}//第一次遍历得到链表的长度(节点个数)int size = getLength(head);//第二次遍历 size-index 位置//先做一个index的校验if(index <= 0 || index > size){return null;}//定义一个辅助变量,for循环定位到倒数的indexHeroNode cur = head.next;//假设链表有3个数据,查找倒数第一个数据,那么size-index=3-1=2for(int i = 0; i < size - index; i++){cur = cur.next;}return cur;}//方法:获取单链表的节点个数(如果是带头节点的链表,那么久不统计头节点)/*** * @param head* @return*/public static int getLength(HeroNode head){if(head.next == null){//空链表return 0;}int length = 0;//定义一个辅助变量,没有统计头节点HeroNode cur = head.next;while(cur != null) {length++;cur = cur.next;//遍历}return length;}
}//定义SingleLinkedList 管理我们的英雄人物
class SingleLinkedList{//先初始化一个头节点,头节点不要动,不存放具体的数据private HeroNode head = new HeroNode(0, null, null);//返回头节点public HeroNode getHead() {return head;}//添加节点到单向链表//思路:当不考虑编号顺序时//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;//标识添加的编号是否已经存在,默认为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对象就是一个节点HeroNode temp = head.next;while (true) {//判断是否到链表最后if(temp == null){break;}//输出节点的信息System.out.println(temp);//将next后移。(不后移就成了死循环,一定小心)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 + ", next = " + next + "]";return "HeroNode [no = " + no + ", name = " + name + ", nickname = " + nickname + "]";}
}

运行结果:

在这里插入图片描述

5.4. 从尾到头打印单链表

(百度面试题)


做这道题之前先学习栈:

代码示例:

package Linkedlist;import java.util.Stack;//stack的基本使用
public class TestStack {public static void main(String[] args){Stack<String> stack = new Stack();//入栈stack.add("jack");stack.add("tom");stack.add("smith");//出栈顺序:smith,tom,jackwhile (stack.size() > 0) {System.out.println(stack.pop());//pop就是将栈顶的数据取出}}}

运行结果:
在这里插入图片描述


思路分析:

在这里插入图片描述

思路
上面的题的要求就是逆序打印单链表.
方式1: 先将单链表进行反转操作,然后再遍历即可,这样的做的问题是会破坏原来的单链表的结构,不建议
方式2:可以利用栈这个数据结构,将各个节点压入到中,然后利用栈的先进后出的特点,就实现了逆序打印的效果。

功能实现:

//逆序打印单链表//方式2:可以利用栈这个数据结构,将各个节点压入到栈中,然后利用栈的先进后出的特点,就实现了逆序打印的效果.public static void reversePrint(HeroNode head){if (head.next == null) {return;//空链表,不能打印}//创建一个栈,将节点压入栈中Stack<HeroNode> stack = new Stack<HeroNode>();HeroNode cur = head.next;//将链表的所有节点压入栈while(cur != null) {stack.push(cur);cur = cur.next;}//将栈中的节点进行打印。pop出栈while (stack.size() > 0) {System.out.println(stack.pop());}}

完整代码实现:

package Linkedlist;import java.net.http.HttpHeaders;
import java.util.Stack;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 singleLinkedList = new SingleLinkedList();//加入按照编号的顺序singleLinkedList.addByOrder(hero1);singleLinkedList.addByOrder(hero4);singleLinkedList.addByOrder(hero2);singleLinkedList.addByOrder(hero3);// singleLinkedList.addByOrder(hero3);//显示链表singleLinkedList.list();System.out.println("-----------逆序打印,没有改变链表的结构--------------");reversePrint(singleLinkedList.getHead());}//逆序打印单链表//方式2:可以利用栈这个数据结构,将各个节点压入到栈中,然后利用栈的先进后出的特点,就实现了逆序打印的效果.public static void reversePrint(HeroNode head){if (head.next == null) {return;//空链表,不能打印}//创建一个栈,将节点压入栈中Stack<HeroNode> stack = new Stack<HeroNode>();HeroNode cur = head.next;//将链表的所有节点压入栈while(cur != null) {stack.push(cur);cur = cur.next;}//将栈中的节点进行打印。pop出栈while (stack.size() > 0) {System.out.println(stack.pop());}}//将单链表反转public static void reversetList(HeroNode head){//如果当前链表为空,或者只有一个节点,无需反转,直接返回if(head.next == null || head.next.next == null){return;}//定义一个辅助的指针(变量),帮助我们遍历原来的链表HeroNode cur = head.next;HeroNode next = null;//指向当前节点[cur]的下一个节点HeroNode reverseHead = new HeroNode(0, "", "");//遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead 的最前端while (cur != null) {next = cur.next;//先暂时保存当前节点的下一个节点,后面需要使用cur.next = reverseHead.next;//将cur的下一个节点指向新的链表的最前端reverseHead.next = cur;cur = next;//让cur后移}//将head.next 指向reverseHead.next,实现单链表的反转head.next = reverseHead.next;}//查找单链表中的倒数第k个节点【新浪面试题】//思路//1. 编写一个方法,接收head节点,同时接收一个index//2. index 表示是倒数第index个节点//3. 先把链表从头到尾遍历,得到链表的总长度getLength//4. 得到size(getLength)后,我们从链表的第一个开始遍历(size-index)个,就可以得到//5. 如果找到了,则返回该节点,否则返回nullpublic static HeroNode findLastIndexNode(HeroNode head, int index){//判断如果链表为空,返回nullif(head.next == null){return null;//没有找到}//第一次遍历得到链表的长度(节点个数)int size = getLength(head);//第二次遍历 size-index 位置//先做一个index的校验if(index <= 0 || index > size){return null;}//定义一个辅助变量,for循环定位到倒数的indexHeroNode cur = head.next;//假设链表有3个数据,查找倒数第一个数据,那么size-index=3-1=2for(int i = 0; i < size - index; i++){cur = cur.next;}return cur;}//方法:获取单链表的节点个数(如果是带头节点的链表,那么久不统计头节点)/*** * @param head* @return*/public static int getLength(HeroNode head){if(head.next == null){//空链表return 0;}int length = 0;//定义一个辅助变量,没有统计头节点HeroNode cur = head.next;while(cur != null) {length++;cur = cur.next;//遍历}return length;}
}//定义SingleLinkedList 管理我们的英雄人物
class SingleLinkedList{//先初始化一个头节点,头节点不要动,不存放具体的数据private HeroNode head = new HeroNode(0, null, null);//返回头节点public HeroNode getHead() {return head;}//添加节点到单向链表//思路:当不考虑编号顺序时//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;//标识添加的编号是否已经存在,默认为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对象就是一个节点HeroNode temp = head.next;while (true) {//判断是否到链表最后if(temp == null){break;}//输出节点的信息System.out.println(temp);//将next后移。(不后移就成了死循环,一定小心)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 + ", next = " + next + "]";return "HeroNode [no = " + no + ", name = " + name + ", nickname = " + nickname + "]";}
}

运行结果:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/156638.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

常见面试题-MySQL的Explain执行计划

了解 Explain 执行计划吗&#xff1f; 答&#xff1a; explain 语句可以帮助我们查看查询语句的具体执行计划。 explain 查出来的各列含义如下&#xff1a; id&#xff1a;在一个大的查询语句中&#xff0c;每个 select 关键字都对应一个唯一的 id select_type&#xff1a;…

SpatialFeaturePlot画图是空的

stmeta.datadplyr::left_join(stmeta.data,coor[,c(3,7:8)],by"barcodes") SpatialFeaturePlot(st,features "test",images "P02") 做了上述操作之后画出的图是空的 原因&#xff0c;left_join之后自动把stmeta.data的行名变成了1&#xff0…

深入了解原型与原型链

1、[[Prototype]] JS中的对象有一个特殊的 [[Prototype]] 内置属性&#xff0c;其实就是对于其他对象的引用。几乎所有的对象在创建时 [[Prototype]] 属性都会被赋予一个非空的值。 var anotherObject {a:2 }; // 创建一个关联到 anotherObject 的对象 var myObject Object…

SSM2

DataSource mybatis与Spring整合 事务加载业务层上面 开启事务驱动 上面都是声明式开启事务 图书管理系统 命名规范: java命名规范:驼峰命名法类:大驼峰变量,属性名.方法名:小驼峰 常量使用下划线分割:全大写,单词与单词之间下划线分割数据库命名规范:常用命名规范:下划线…

排名全球前列!Flat Ads再入选AppsFlyer广告榜单

近期&#xff0c;移动归因与营销分析公司AppsFlyer《广告平台综合表现报告》第16版重磅发布&#xff01;盘点全球买量渠道表现&#xff0c;洞察移动营销行业最新格局。其中Flat Ads凭借实力和体量&#xff0c;入选AppsFlyer广告平台综合表现非游戏类Top级流量媒体4个全球榜单排…

大数据Doris(二十六):数据导入(Routine Load)介绍

文章目录 数据导入(Routine Load)介绍 一、适用场景

Rockchip Clock

一:概述 1、时钟子系统 本章节所指的时钟是给SOC各个组件提供时钟的树状框架,而非内核使用的时钟。和其他模块一样,CLOCK也有框架,用以适配不同的平台。适配层之上是客户代码和接口,也就是各模块(如需要时钟信号的外设)的驱动。适配层之下是具体的SOC的时钟操作细节。…

智慧城市智能井盖传感器有哪些特点

在不断变化的城市发展格局中&#xff0c;智慧城市的建设毋庸置疑是很重要的&#xff0c;这会加快城市化进程并促进城市的高质量发展。每一个城市在技术和创新的推动下&#xff0c;提高居民的生活质量&#xff0c;同时确保城市发展的可持续性和政府的办事效率稳步提升。就像是智…

C++ STL -->string类

文章目录 STL什么是STL String类string类对象的构造方式string类对象的容量操作string类对象的访问及遍历操作string迭代器函数遍历类对象 stirng类对象的修改操作string类非成员函数 STL 什么是STL STL全称standard template libaray-标准模板库 是C标准库的重要组成部分 不…

SpringCloud01

SpringCloud01 1.认识微服务 随着互联网行业的发展&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢&#xff1f; 1.0.学习目标 了解微服务架构的优缺点 1.1.单体架构 单体架构&#xff…

【腾学汇的第1个实验代码】应用Matplotlib绘制图标分析

应用Matplotlib绘制图标分析 # 1.创建一维数组 # 导入包 import numpy as np#两种方式创建一维数组 vector np.array([1,2,3]) #把数列转换成 nampy格式的数组 print(vector)print(type([1, 2, 3]), type(vector)) vector np.arange(15) #使用numpy.arange()方法创建给定区…

吉他效果器开发方法

吉他效果器开发方法 是否需要申请加入数字音频系统研究开发交流答疑群(课题组)&#xff1f;可加我微信hezkz17, 本群提供音频技术答疑服务&#xff0c;群赠送语音信号处理降噪算法&#xff0c;蓝牙耳机音频&#xff0c;ADI DSP音频项目核心开发资料, 1 做出的效果图 2 介绍 …

【网络通信】探索UDP与TCP协议、IP地址和端口号的奥妙

&#x1f33a;个人主页&#xff1a;Dawn黎明开始 &#x1f380;系列专栏&#xff1a;网络奇幻之旅 ⭐每日一句&#xff1a;往前走&#xff0c;朝着光 &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️ 文章目录 &#x1f4cb;前…

【项目管理】甘特图(2)——甘特图教程

哈喽啊&#xff0c;你好&#xff0c;我是雷工。 通过上节初步认识了甘特图&#xff0c;本节学习如何一步步创建甘特图&#xff0c;以下为学习笔记。 一、样例展示 下边记录创建甘特图的操作步骤&#xff0c;完成的实际效果如下图所示&#xff1a; 实例图的上端展示项目的重要…

Altium Designer学习笔记7

PCB封装库的制作&#xff1a; 距离的测量&#xff1a; 各个焊盘的位置&#xff1a; 直插元件选择Multi-Layer。如果贴片元件的则选择顶层Top-Layer&#xff0c;或者Bottom-Layer。 形状是方形&#xff0c;尺寸是2mm*2mm。 孔的尺寸是1.4mm。 则该器件就制作完成。 TSSOP28封装…

VirtualBox7安装Ubuntu20及全屏、共享、粘贴板等设置

VirtualBox7安装Ubuntu20及全屏、共享、粘贴板等设置 1. 安装VirtualBox2. 安装Ubuntu202.1 获得x.vdi2.2 新建虚拟电脑 3. 增强设置3.1 增强功能3.2 全屏3.2 共享文件夹3.4 粘贴板 1. 安装VirtualBox 官网https://www.virtualbox.org/wiki/Downloads下载&#xff0c;笔者选择…

MyCAT2分库分表

目录 ​​​​​​一、分库分表的原理 1.垂直切分 2.水平切分 3.如何分表 1.选择要拆分的表 2.选择分表的定段 3.分库分表的环境准备 1.进入dw0配置 1&#xff09;进入目录&#xff1a; 2&#xff09;修改dw0.cnf&#xff1a; 3&#xff09;修改dr0.cnf&#xff1a;…

# 聚类系列(一)——什么是聚类?

目前在做聚类方面的科研工作, 看了很多相关的论文, 也做了一些工作, 于是想出个聚类系列记录一下, 主要包括聚类的概念和相关定义、现有常用聚类算法、聚类相似性度量指标、聚类评价指标、 聚类的应用场景以及共享一些聚类的开源代码 下面正式进入该系列的第一个部分&#xff…

做好性能测试计划的4个步骤!全都是精华!【建议收藏】

如何做好一次性能测试计划呢&#xff1f;对于性能测试新手来说&#xff0c;也许你非常熟悉Jmeter的使用&#xff0c;也许你清楚的了解每一个系统参数代表的意义&#xff0c;但是想要完成好一次性能测试任务&#xff0c;并不仅仅是简单的写脚本&#xff0c;加压力&#xff0c;再…

【Seata源码学习 】篇三 TM开启全局事务的过程

【Seata源码学习 】篇三 TM开启全局事务的过程 TM发送 单个或批量 消息 以发送GlobalBeginRequest消息为例 TM在执行拦截器链路前将向TC发送GlobalBeginRequest 消息 io.seata.tm.api.DefaultGlobalTransaction#begin(int, java.lang.String) Overridepublic String begin(…