• 【数据结构】单向链表


    一、链表的介绍

      1、链表是以节点的方式来储存,是链式储存。

      2、每个节点包含data域(存放数据)、next域(指向下一个节点)。

      3、链表的各个节点不一定是连续储存的。

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

    二、带头节点单链表示意图

      

    三、编写java代码实现带头节点的单链表

    3.1 应用实例

      使用带头的单向链表实现 – 水浒英雄排行榜管理完成对英雄人物的增删改查操作。

    3.2 编写HeroNode类

      定义HeroNode,每个HeroNode对象就是一个节点。

    /*
       定义HeroNode,每个HeroNode对象就是一个节点
     */
    public 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方法
        @Override
        public String toString() {
            return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
        }
    }

    3.3 编写SingleLinkedList类

      1 package point5;
      2 
      3 import java.util.Stack;
      4 
      5 // 定义 SingleLinkedList 管理我们的英雄
      6 public class SingleLinkedList {
      7     // 现初始化一个头节点,头节点不要动,不存放具体的数据
      8     private HeroNode head = new HeroNode(0,"","");
      9 
     10     // 一、添加节点到单向链表
     11     // 思路,当不考虑编号顺序时
     12     // 1、找到当前链表的最后节点
     13     // 2、将最后这个节点的 next 指向新的节点
     14     public void add(HeroNode heroNode){
     15         // 因为 head 节点不能动,因此我们需要一个辅助遍历 temp
     16         HeroNode temp = head;
     17         // 遍历链表,找到最后
     18         while (true){
     19             // 找到链表的最后
     20             if (temp.next == null){
     21                 break;
     22             }
     23             // 如果没有找到最后,将temp后移
     24             temp = temp.next;
     25         }
     26         // 当退出while循环时,temp就指向了链表的最后
     27         // 将最后这个节点的next指向新的节点
     28         temp.next = heroNode;
     29     }
     30 
     31     // 二、按照no从小到大的顺序添加节点到单向链表
     32     // 第二种方式在添加英雄时,根据排名将英雄插入到指定位置
     33     // 如果有这个排名,则添加失败,并给出提示
     34     public void addByOrder(HeroNode heroNode){
     35         // 因为头节点不能动,因此我们仍然通过一个辅助指针(变量)来帮助找到添加的位置
     36         // 因为单链表,因为我们找的 temp 是位于添加位置的前一个节点,否则插入不了
     37         HeroNode temp = head;
     38         // 标志添加的编号是否存在,默认为false
     39         boolean flag = false;
     40         while (true){
     41             // 说明 temp 已经在链表的最后
     42             if (temp.next == null){
     43                 break;
     44             }
     45             // 位置找到,就在 temp 的后面插入
     46             if (temp.next.no > heroNode.no) {
     47                 break;
     48             }
     49             // 说明希望添加的 heroNode 的编号已然存在
     50             else if (temp.next.no == heroNode.no){
     51                 //说明编号存在
     52                 flag = true;
     53                 break;
     54             }
     55             // 后移,遍历当前链表
     56             temp = temp.next;
     57         }
     58         // 判断 flag 的值
     59         // 不能添加,说明编号存在
     60         if (flag){
     61             System.out.printf("准备插入的英雄的编号 %d 已经存在了,不能加入
    ", heroNode.no);
     62         }
     63         else {
     64             // 插入到链表中,temp的后面
     65             heroNode.next = temp.next;
     66             temp.next = heroNode;
     67         }
     68     }
     69 
     70     // 三、修改节点的信息,根据no编号来修改,即no编号不能改
     71     // 说明
     72     // 1、根据 newHeroNode 的 no 来修改即可
     73     public void update(HeroNode newHeroNode){
     74         // 判断是否空
     75         if(head.next == null){
     76             System.out.println("链表为空");
     77             return;
     78         }
     79         // 找到需要修改的节点,根据 no 编号
     80         // 定义一个辅助变量
     81         HeroNode temp = head.next;
     82         // 表示是否找到该节点
     83         boolean flag = false;
     84         while(true){
     85             // 已遍历完链表
     86             if (temp == null){
     87                 break;
     88             }
     89             if (temp.no == newHeroNode.no){
     90                 // 找到
     91                 flag = true;
     92                 break;
     93             }
     94             temp = temp.next;
     95         }
     96         // 根据 flag 判断是否找到要修改的节点
     97         if(flag){
     98             temp.name = newHeroNode.name;
     99             temp.nickname = newHeroNode.nickname;
    100         }
    101         // 没有找到
    102         else {
    103             System.out.printf("没有找到编号%d的节点,不能修改
    ", newHeroNode.no);
    104 
    105         }
    106     }
    107 
    108     // 四、删除节点
    109     // 思路
    110     // 1、head 不能动,因此我们需要一个 temp 辅助节点找到待删除节点的前一个节点
    111     // 2、说明我们在比较的时,是 temp.next.no 和需要删除的节点的 no 比较
    112     public void del(int no){
    113         HeroNode temp = head;
    114         boolean flag = false; //标志是否找到待删除节点的no
    115         while (true){
    116             // 已经到链表的最后
    117             if (temp.next == null) {
    118                 break;
    119             }
    120             // 找到的待删除节点的前一个节点temp
    121             if (temp.next.no == no){
    122                 // 找到的待删除节点的前一个节点 temp
    123                 flag = true;
    124                 break;
    125             }
    126             // temp后移,遍历
    127             temp = temp.next;
    128         }
    129         // 判断 flag
    130         if(flag){ // 找到
    131             // 可以删除
    132             temp.next = temp.next.next;
    133         }
    134         else {
    135             System.out.printf("要删除的%d节点不存在
    ",no);
    136         }
    137     }
    138 
    139     // 五、显示链表
    140     public void list(){
    141         // 判断链表是否为空
    142         if (head.next == null){
    143             System.out.println("链表为空");
    144             return;
    145         }
    146         // 因为头节点,不能动,因此我们需要一个辅助变量来遍历
    147         HeroNode temp = head.next;
    148         while (true){
    149             // 判断是否到链表最后
    150             if (temp == null){
    151                 break;
    152             }
    153             // 输出节点的信息
    154             System.out.println(temp);
    155             // 将 temp 后移
    156             temp = temp.next;
    157         }
    158     }
    159 
    160     // 六、反转链表
    161     public  void  reverseList(){
    162         // 如果当前链表为空,或者只有一个节点,无需反转,直接返回
    163         if (head.next == null || head.next.next == null){
    164             return;
    165         }
    166 
    167         // 定义一个辅助的指针(变量),帮助我们遍历原来的链表
    168         HeroNode cur = head.next;
    169         // 指向当前节点[cur]的下一个节点
    170         HeroNode next = null;
    171         HeroNode reverseHead = new HeroNode(0, "", "");
    172         // 遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表 reverseHead 的最前端
    173         while (cur != null){
    174             next = cur.next; // 先暂时保存当前节点的下一个节点,后面需要使用
    175             cur.next = reverseHead.next; // 将cur的下一个节点指向新的链表
    176             reverseHead.next = cur; // 将cur连接到新的链表上
    177             cur = next; // 让cur后移
    178         }
    179         // 将 head.next 指向 reverseHead.next,实现单链表的反转
    180         head.next = reverseHead.next;
    181     }
    182 
    183     // 七、求单链表中的有效节点的个数
    184     public  int  getLength(){
    185         /**
    186          * @description:
    187          * @return: 返回的就是有效节点的个数
    188          * @param: head 链表的头节点
    189          * @author: hyr
    190          * @time: 2020/1/13 20:11
    191          */
    192         if(head.next == null){ // 空链表
    193             return 0;
    194         }
    195         int length = 0;
    196         // 定义一个辅助的变量,这里我们没有统计头节点
    197         HeroNode cur = head.next;
    198         while (cur != null){
    199             length++;
    200             cur = cur.next;
    201         }
    202         return length;
    203     }
    204 
    205     // 八、逆序打印链表
    206     public void reversePrint() {
    207         /**
    208          * @description: 使用栈这个数据结构,将各个节点压入栈中,再弹出就可以实现逆序打印的效果
    209          * @return:
    210          * @param: head 单链表的头节点
    211          * @author: hyr
    212          * @time: 2020/1/13 21:21
    213          */
    214         if (head.next == null) {
    215             return; // 空链表,不能打印
    216         }
    217         // 创建要给一个栈,将各个节点压入栈
    218         Stack<HeroNode> stack = new Stack<>();
    219         HeroNode cur = head.next;
    220         // 将链表中的所有节点压入栈
    221         while (cur != null) {
    222             stack.push(cur);
    223             cur = cur.next; //cur后移,这一就可以压入下一个节点
    224         }
    225         // 将栈中的节点进行打印,pop出栈
    226         while (stack.size() > 0) {
    227             System.out.println(stack.pop()); // 栈的特点是先进后出
    228         }
    229     }
    230 
    231     // 九、查找单链表中的倒数第k个结点
    232     public HeroNode findLastIndexNode(int index){
    233         // 思路
    234         // 1、编写一个方法,接受 head 节点,同时接收一个index
    235         // 2、index表示是倒数第 index 个节点
    236         // 3、先把链表从头到尾遍历,得到链表的总的长度 getLength
    237         // 4、得到 size 后,我们从链表的第一个开始遍历(size-index)个,就可以得到
    238         // 5、如果找到了,则返回该节点,否则返回null
    239 
    240         // 判断如果链表为空,返回null
    241         if (head.next == null){
    242             System.out.println("没有找到");
    243         }
    244 
    245         // 第一个遍历得到链表的长度(节点个数)
    246         int size = getLength();
    247 
    248         // 第二次遍历 size-index 位置,就是我们倒数的第K个节点
    249         // 先做一个 index 的校验
    250         if(index <= 0 || index > size){
    251             System.out.println("要查找的位置不存在");
    252         }
    253 
    254         // 定义给辅助变量,for循环定位到倒数的index
    255         HeroNode cur = head.next;
    256         for (int i = 0; i < size - index; i++) {
    257             cur = cur.next;
    258         }
    259 
    260         return cur;
    261     }
    262 }

    3.4 编写测试类SingleLinkedListDemo

     1 package point5;
     2 
     3 /*
     4     使用带 head 头的单向链表实现 -- 水浒英雄排行榜管理, 完成对英雄人物的增删改查操作。
     5  */
     6 public class SingleLinkedListDemo {
     7     public static void main(String[] args) {
     8         // 进行测试
     9         // 先创建节点
    10         HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
    11         HeroNode hero2 = new HeroNode(2, "卢俊义", "玉麒麟");
    12         HeroNode hero3 = new HeroNode(3, "吴用", "智多星");
    13         HeroNode hero4 = new HeroNode(4, "林冲", "豹子头");
    14 
    15         // 创建链表
    16         SingleLinkedList singleLinkedList = new SingleLinkedList();
    17 
    18 //        // 一、不按顺序添加节点,进行测试
    19 //        singleLinkedList.add(hero1);
    20 //        singleLinkedList.add(hero2);
    21 //        singleLinkedList.add(hero4);
    22 //        singleLinkedList.add(hero3);
    23 //        //查看一下链表的信息
    24 //        singleLinkedList.list();
    25 
    26         // 二、按照no顺序添加节点,进行测试
    27         singleLinkedList.addByOrder(hero4);
    28         singleLinkedList.addByOrder(hero2);
    29         singleLinkedList.addByOrder(hero1);
    30         singleLinkedList.addByOrder(hero3);
    31         // 查看一下链表信息
    32         System.out.println("按序插入后的链表信息为:");
    33         singleLinkedList.list();
    34         System.out.println();
    35 
    36         // 三、修改节点的信息
    37         singleLinkedList.update(new HeroNode(1,"宋宋","小宋"));
    38         // 修改后的链表信息
    39         System.out.println("修改后的链表信息为:");
    40         singleLinkedList.list();
    41         System.out.println();
    42 
    43         // 四、删除节点
    44         singleLinkedList.del(1);
    45         // 删除节点后的链表信息
    46         System.out.println("删除节点后的链表信息为:");
    47         singleLinkedList.list();
    48         System.out.println();
    49 
    50         // 五、显示链表
    51         // 当前链表信息
    52         System.out.println("当前链表信息为:");
    53         singleLinkedList.list();
    54         System.out.println();
    55 
    56         // 六、反转链表
    57         System.out.println("反转后的链表信息为:");
    58         singleLinkedList.reverseList();
    59         singleLinkedList.list();
    60         System.out.println();
    61 
    62         // 七、求链表中有效节点的个数
    63         System.out.println("链表中有效节点的个数为:");
    64         System.out.println(singleLinkedList.getLength());
    65         System.out.println();
    66 
    67         // 八、逆序打印链表
    68         System.out.println("逆序打印的链表信息为:");
    69         singleLinkedList.reversePrint();
    70         System.out.println();
    71 
    72         // 九、查找链表中的倒数第k个结点
    73         System.out.println("链表中的倒数第一个节点信息为:");
    74         System.out.println(singleLinkedList.findLastIndexNode(1));
    75     }
    76 }

    3.5 代码运行结果

    按序插入后的链表信息为:
    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=豹子头]
    
    删除节点后的链表信息为:
    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=豹子头]
    
    反转后的链表信息为:
    HeroNode [no=4, name=林冲, nickname=豹子头]
    HeroNode [no=3, name=吴用, nickname=智多星]
    HeroNode [no=2, name=卢俊义, nickname=玉麒麟]
    
    链表中有效节点的个数为:
    3
    
    逆序打印的链表信息为:
    HeroNode [no=2, name=卢俊义, nickname=玉麒麟]
    HeroNode [no=3, name=吴用, nickname=智多星]
    HeroNode [no=4, name=林冲, nickname=豹子头]
    
    链表中的倒数第一个节点信息为:
    HeroNode [no=2, name=卢俊义, nickname=玉麒麟]

    原文链接:https://blog.csdn.net/a1786742005/article/details/103978011

  • 相关阅读:
    FCOS及其和Faster R-CNN的区别
    CornerNet: Detecting Objects as Paired Keypoints
    神经网络不收敛的原因
    交叉熵损失函数
    Placeholder_2:0 is both fed and fetched
    使用Lambda解决_inbound_nodes错误
    Python对Dict排序
    对Faster R-CNN的理解(3)
    Keras运行速度越来越慢的问题
    深度卷积网络(DCNN)和人类识别物体方法的不同
  • 原文地址:https://www.cnblogs.com/h--d/p/14553100.html
Copyright © 2020-2023  润新知