当前位置: 首页 > news >正文

【数据结构(二)】单链表(3)

文章目录

  • 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 + "]";}
}

运行结果:

在这里插入图片描述

相关文章:

【数据结构(二)】单链表(3)

文章目录 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.…...

创新案例|云服务平台HashiCorp是如何构建开源社区实现B2B增长飞轮

社区文化是HashiCorp企业文化的重要组成部分。虽然众多公司声称自己是社区驱动&#xff0c;但实际付诸行动的很少。与众不同的是&#xff0c;HashiCorp从一开始就将社区视为战略方针的核心&#xff0c;这也影响和塑造了公司今天的发展方向。社区不仅是执行策略之一&#xff0c;…...

2024年软件测试面试必看系列,看完去面试你会感谢我的!!

朋友圈点赞的测试用例 功能测试 1点赞后是否显示结果 2.点赞后是否可以取消; 3.点赞取消后是否可以重复点赞; 4.共同好友点赞后&#xff0c;是否有消息提醒; 5.非共同好友点赞后&#xff0c;是否有消息提醒; 6.点击点赞人昵称&#xff0c;是否可以跳转到他/她的主页; 7.自己能…...

01ctfer 文件上传

01ctfer 文件上传 启动靶场 访问该地址 代码审计 <?php header("Content-Type:text/html; charsetutf-8"); // 每5分钟会清除一次目录下上传的文件 require_once(pclzip.lib.php);if(!$_FILES){echo <!DOCTYPE html> <html lang"zh">…...

2.2 调用星火大模型的API

调用星火大模型的API 1 申请API调用权限&#xff1a;2 调用原生星火 API3 统一API调用方式 项目仓库地址&#xff1a;https://github.com/datawhalechina/llm-universe 讯飞星火认知大模型&#xff0c;由科大讯飞于2023年5月推出的中文大模型&#xff0c;也是国内大模型的代表…...

云原生是整个信息化行业的未来,一文彻底搞懂云原生

云原生这个词来自英语的Cloud Native的翻译&#xff0c;云原生是已经存多年在术语&#xff0c;真正开始获得关注的是在2015年到2016年。 这归因于这几年逐渐发布的Docker的兴起。 会有越来越多的企业和组织开始关注到它&#xff0c;并把他们的工作负载运行在云端的益处。无论是…...

【Redis】RedisTemplate最全的常用方法

文章目录 前言1.RedisTemplate常用方法2.String类型3.Hash类型4.List类型5.Set类型6.zSet类型 前言 RedisTemplate常用方法String类型Hash类型List类型Set类型zSet类型 Redis常用的数据类型&#xff1a;String、Hash、List、Set、zSet 1.RedisTemplate常用方法 redisTempla…...

图像倾斜角度求取-Radon变换

Radon算法 Radon&#xff08;拉东&#xff09;算法是一种通过定方向投影叠加&#xff0c;找到最大投影值时角度&#xff0c;从而确定图像倾斜角度的算法。具体过程如图所示 图1 Radon变换算法 Radon计算示例 对于纹理方向明显的图像&#xff0c;如图2所示&#xff0c;可以通…...

如何在本地搭建Oracle数据库实现公网环境下通过PLSQL工具进行远程访问

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 前言 Oracle&#xff0c;是甲骨文公司的一款关系…...

时序预测 | Python实现ConvLSTM卷积长短期记忆神经网络股票价格预测(Conv1D-LSTM)

时序预测 | Python实现ConvLSTM卷积长短期记忆神经网络股票价格预测(Conv1D-LSTM) 目录 时序预测 | Python实现ConvLSTM卷积长短期记忆神经网络股票价格预测(Conv1D-LSTM)预测效果基本介绍程序设计参考资料预测效果 基本介绍 时序预测 | Python实现ConvLSTM卷积长短期记忆神…...

qtpdfium的编译及读取pdf文件和一些简单操作

qtpdfium是谷歌的一款开源项目&#xff0c;它的内核是基于国内的福昕pdf&#xff0c;许可协议为 BSD 3-Clause&#xff0c;允许用于闭源商业行为 下载 我们可以从git上进行下载&#xff0c;github&#xff0c;如果嫌下载速度慢&#xff0c;可以从csdn进行下载csdn 下载完成之…...

ClickHouse查看执行计划

在clickhouse 20.6版本之前要查看SQL语句的执行计划需要设置日志级别为trace才能可以看到&#xff0c;并且只能真正执行sql&#xff0c;在执行日志里面查看。在20.6版本引入了原生的执行计划的语法。在20.6.3版本成为正式版本的功能。 本文档基于目前较新稳定版21.7.3.14。 1.基…...

2023-11-17 VsCode使用makefile进行多文件编译

点击 <C 语言编程核心突破> 快速C语言入门 VsCode使用makefile进行多文件编译 前言一、一个简单的多文件示例二、makefile基本语法三、VsCode使用makefile总结 前言 要解决问题: C或C可以多文件编译, 意味着需要进行代码组织, 为了方便多文件编译, gnu开发了make工具, …...

Network(四)NAT实现方式与VRRP概述

一 NAT 1 NAT概述 &#xff08;1&#xff09;NAT的作用 Network Address Translation&#xff0c;网络地址转换 通过将内部网络的私有IP地址转换成全球唯一的公网IP地址使内部网络可以连接到互联网。 &#xff08;2&#xff09;私有IP地址分类 A类10.0.0.0~10.255.255.…...

C#_键盘钩子

一、class class KeyboardHook{public event KeyEventHandler KeyDownEvent;public event KeyPressEventHandler KeyPressEvent;public event KeyEventHandler KeyUpEvent;public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);static int hKeyboardHook 0;…...

YOLO免费数据集网站收集

目录 Roboflow Universe: Open Source Computer Vision Community Find Open Datasets and Machine Learning Projects | Kaggle ​编辑 【火焰和烟雾图像数据集】-计算机视觉数据集-极市开发者平台 (cvmart.net) 开放数据集- 飞桨AI Studio星河社区 - 人工智能学习与实训社…...

拼图小游戏

package li;import ui.tu; //启动类 public class 主 {public static void main(String[] args) {new tu(); //创建登陆界面} }package ui;import javax.swing.*; import javax.swing.border.BevelBorder; import java.awt.event.ActionEvent; import java.awt.event.ActionLi…...

卷积神经网络(CNN)天气识别

文章目录 前期工作1. 设置GPU&#xff08;如果使用的是CPU可以忽略这步&#xff09;我的环境&#xff1a; 2. 导入数据3. 查看数据 二、数据预处理1. 加载数据2. 可视化数据3. 再次检查数据4. 配置数据集 三、构建CNN网络四、编译五、训练模型六、模型评估 前期工作 1. 设置GP…...

Linux进程间通信之匿名管道

文章目录 为什么要有进程间通信pipe函数共享管道原理管道特点管道的四种情况 管道的应用场景&#xff08;进程池&#xff09;ProcessPool.ccTask.hpp 为什么要有进程间通信 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程 资源共享&#xff1a;多个进程之间共享…...

【PTA题目】6-19 使用函数输出指定范围内的Fibonacci数 分数 20

6-19 使用函数输出指定范围内的Fibonacci数 分数 20 全屏浏览题目 切换布局 作者 C课程组 单位 浙江大学 本题要求实现一个计算Fibonacci数的简单函数&#xff0c;并利用其实现另一个函数&#xff0c;输出两正整数m和n&#xff08;0<m≤n≤10000&#xff09;之间的所有F…...

IGP(Interior Gateway Protocol,内部网关协议)

IGP&#xff08;Interior Gateway Protocol&#xff0c;内部网关协议&#xff09; 是一种用于在一个自治系统&#xff08;AS&#xff09;内部传递路由信息的路由协议&#xff0c;主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...

渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止

<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet&#xff1a; https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架&#xff0c;用于…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)

考察一般的三次多项式&#xff0c;以r为参数&#xff1a; p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]&#xff1b; 此多项式的根为&#xff1a; 尽管看起来这个多项式是特殊的&#xff0c;其实一般的三次多项式都是可以通过线性变换化为这个形式…...

mac 安装homebrew (nvm 及git)

mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用&#xff1a; 方法一&#xff1a;使用 Homebrew 安装 Git&#xff08;推荐&#xff09; 步骤如下&#xff1a;打开终端&#xff08;Terminal.app&#xff09; 1.安装 Homebrew…...