数据结构与算法 ------ 打卡第三天 链表

1. 链表

链表( linked list) 是一种在物理上非连续、 非顺序的数据结构, 以节点的方式来存储,是链式存储。

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RQKrejiF-1602656421578)(数据结构与算法img/.png)]

由图可知:

  • 每个节点包含 data 域, next 域:指向下一个节点.

  • 发现链表的 各个节点不一定是连续存储.

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

1.1 单链表

1.1.1 介绍

单向链表的每一个节点又包含两部分, 一部分是存放数据的变量data, 另一部分是指向下一个节点的指针next。

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NQXkDbGj-1602656421581)(数据结构与算法img/单链表.png)]

private static class Node {
    int data;
    Node next;
}

链表的第1个节点被称为头节点, 最后1个节点被称为尾节点, 尾节点的next指针指向空。

1.1.2 应用实例

使用带head头的单向链表实现 –水浒英雄排行榜管理(增删改查)

  1. 添加英雄(增加)

    方式:

    • 直接添加到链表的 尾部

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FsF04pLY-1602656421584)(数据结构与算法img/单链表-增.png)]

      代码实现:

      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,"","");
      
          // 添加节点到单向链表
          // 1.  不考虑编号顺序的情况,添加到链表的 尾部
          //      思路:
          //          a. 通过遍历找到当前链表的最后节点
          //          b. 将最后这个节点的 next 指向 新节点
          public void  add(HeroNode heroNode){
              // 因为 head 节点不能动,因此需要一个辅助变量 temp
              HeroNode temp=head;
      
              // 遍历链表,找到链表最后节点
              while(true){
                  //  找到链表最后节点
                  if(temp.next == null){
                      break;
                  }
      
                  // 如果没有找到最后节点,就将 temp 后移
                  temp=temp.next;
              }
      
              // 当退出 while 循环时,temp就指向了链表的最后节点
              // 将最后这个节点的 next 指向 新的节点
              temp.next=heroNode;
          }
      
          // 显示链表【遍历】
          public void list(){
              //  判断链表是否为空
              if(head.next == null){
                  System.out.println("链表为空");
                  return;
              }
      
              // 因为头节点不能动,因此需要一个辅助变量来遍历
              HeroNode temp = head.next;
              while (true){
                  // 判断是否到链表最后
                  if(temp == null){
                      break;
                  }
                  // 输出节点信息
                  System.out.println(temp);
      
                  // 将 temp 后移
                  temp=temp.next;
              }
          }
      }
      
      
      // 定义HeroNode,每个 HeroNode 对象就是一个节点
      class HeroNode{
          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+" ]";
          }
      }
      

      输出结果:

      HeroNode [ no = 1 , name = 宋江 , nickname = 及时雨 ]
      HeroNode [ no = 2 , name = 卢俊义 , nickname = 玉麒麟 ]
      HeroNode [ no = 3 , name = 吴用 , nickname = 智多星 ]
      HeroNode [ no = 4 , name = 林冲 , nickname = 豹子头 ]
      
    • 根据编号 将英雄插入到 指定位置 (如果已经存在这个编号,则添加失败,并给出提示)

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8O6m1oUE-1602656421588)(数据结构与算法img/单链表-增1.png)]

      代码实现:

      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(hero2);
              singleLinkedList.addByOrder(hero3);
              singleLinkedList.addByOrder(hero4);
              
              singleLinkedList.addByOrder(hero3);   //  这里会提示错误信息
      
              // 显示链表
              singleLinkedList.list();
          }
      }
      
      // 定义 SingleLinkedList 管理英雄
      class SingleLinkedList{
          // 先初始化一个头结点,头结点,不存放具体的数据
          private HeroNode head=new HeroNode(0,"","");
      
          // 添加节点到单向链表
          // 2.考虑编号顺序的情况,根据编号 将英雄插入到 指定位置 (如果有这个编号,则添加失败,并给出提示)
          //      思路:
          //          a. 通过辅助变量temp遍历找到新添加的节点的要添加的位置
          //          b. 新节点.next = temp.next;
          //              temp.next = 新节点
          public void  addByOrder(HeroNode heroNode){
              //  因为头节点不能动,通过一个辅助变量来帮组找到添加的位置
              //  因为单链表,因为找的 temp 是位于 添加位置的前一个节点,否则插入不了
              HeroNode temp=head;
              boolean flag= false;  // flag 标识添加的编号是否存在,默认为false
              while (true){
                  if(temp.next==null){  //  说明 temp 已经在链表的最后
                      break;
                  }
                  if(temp.next.no> heroNode.no){  // 位置找到,就在 temp 的后面插入
                      break;
                  }else if(temp.next.no == heroNode.no){  // 说明希望添加的 heroNode 的编号已经存在了
                      flag= true;   // 说明编号存在
                      break;
                  }
                  temp=temp.next;  // 后移,遍历当前链表
              }
      
              // 判断 flag 的值
              if(flag){   //  不能添加,说明编号存在
                  System.out.printf("准备插入的英雄的编号 %d 已经存在了,不能加入\n",heroNode.no);
              }else{
                  //  插入到链表中,temp的后面
                  heroNode.next=temp.next;
                  temp.next=heroNode;
              }
          }
      
      
          // 显示链表【遍历】
          public void list(){
              //  判断链表是否为空
              if(head.next == null){
                  System.out.println("链表为空");
                  return;
              }
      
              // 因为头节点不能动,因此需要一个辅助变量来遍历
              HeroNode temp = head.next;
              while (true){
                  // 判断是否到链表最后
                  if(temp == null){
                      break;
                  }
                  // 输出节点信息
                  System.out.println(temp);
      
                  // 将 temp 后移
                  temp=temp.next;
              }
          }
      }
      
      
      // 定义HeroNode,每个 HeroNode 对象就是一个节点
      class HeroNode{
          public int no;  // 编号
          public String name;   // 名字
          public String nickname;  // 昵称
          public HeroNode next;  // 指向下一个节点
      
          // 构造器
          public HeroNode(int no,String name,String nickname){
              this.no=no;
              this.name=name;
              this.nickname=nickname;
          }
      
          // 为了显示方法,重写 toString
          @Override
          public  String toString(){
              return "HeroNode [ no = "+no+" , name = "+name+" , nickname = "+nickname+" ]";
          }
      }
      

      输出结果:

      准备插入的英雄的编号 3 已经存在了,不能加入
      HeroNode [ no = 1 , name = 宋江 , nickname = 及时雨 ]
      HeroNode [ no = 2 , name = 卢俊义 , nickname = 玉麒麟 ]
      HeroNode [ no = 3 , name = 吴用 , nickname = 智多星 ]
      HeroNode [ no = 4 , name = 林冲 , nickname = 豹子头 ]
      
  2. 删除英雄(删除)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9W2Bz1Vh-1602656421590)(数据结构与算法img/单链表-删.png)]

    代码实现:

    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(hero2);
            singleLinkedList.addByOrder(hero3);
            singleLinkedList.addByOrder(hero4);
    
            // 显示链表
            singleLinkedList.list();
    
            //  删除一个节点
            singleLinkedList.del(1);
            System.out.println("删除后的链表情况:");
            singleLinkedList.list();
        }
    }
    
    // 定义 SingleLinkedList 管理英雄
    class SingleLinkedList{
        // 先初始化一个头结点,头结点,不存放具体的数据
        private HeroNode head=new HeroNode(0,"","");
    
        // 添加节点到单向链表
        // 2.  考虑编号顺序的情况,根据排名 将英雄插入到 指定位置 (如果有这个排名,则添加失败,并给出提示)
        //      思路:
        //          a. 通过辅助变量temp遍历找到新添加的节点的要添加的位置
        //          b. 新节点.next = temp.next;
        //              temp.next = 新节点
        public void  addByOrder(HeroNode heroNode){
            //  因为头节点不能动,通过一个辅助变量来帮组找到添加的位置
            //  因为单链表,因为找的 temp 是位于 添加位置的前一个节点,否则插入不了
            HeroNode temp=head;
            boolean flag= false;  // flag 标识添加的编号是否存在,默认为false
            while (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;
            }
        }
        
    
        // 删除节点
        // 思路
        // 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){
                    //  找到的待删除节点的前一个节点 temp
                    flag=true;
                    break;
                }
                temp=temp.next;    // temp后移,遍历当前链表
            }
             // 判断 flag
            if(flag){   // 找到
                //  可以删除
                temp.next=temp.next.next;
            }else{
                System.out.printf("要删除的 %d 节点不存在\n",no);
            }
        }
    
    
        // 显示链表【遍历】
        public void list(){
            //  判断链表是否为空
            if(head.next == null){
                System.out.println("链表为空");
                return;
            }
    
            // 因为头节点不能动,因此需要一个辅助变量来遍历
            HeroNode temp = head.next;
            while (true){
                // 判断是否到链表最后
                if(temp == null){
                    break;
                }
                // 输出节点信息
                System.out.println(temp);
    
                // 将 temp 后移
                temp=temp.next;
            }
        }
    }
    
    
    // 定义HeroNode,每个 HeroNode 对象就是一个节点
    class HeroNode{
        public int no;  // 编号
        public String name;   // 名字
        public String nickname;  // 昵称
        public HeroNode next;  // 指向下一个节点
    
        // 构造器
        public HeroNode(int no,String name,String nickname){
            this.no=no;
            this.name=name;
            this.nickname=nickname;
        }
    
        // 为了显示方法,重写 toString
        @Override
        public  String toString(){
            return "HeroNode [ no = "+no+" , name = "+name+" , nickname = "+nickname+" ]";
        }
    }
    
  3. 修改英雄信息(修改)

    思路:

    • 通过遍历,先找到该节点.

    • temp.name = newHeroNode.name;

      temp.nickname = newHeroNode.nickname;

    代码实现:

    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(hero2);
            singleLinkedList.addByOrder(hero3);
            singleLinkedList.addByOrder(hero4);
    
            // 显示链表
            singleLinkedList.list();
            System.out.println();
            
            //  测试修改节点的代码
            HeroNode newHeroNode = new HeroNode(2,"小卢","玉麒麟~");
            singleLinkedList.update(newHeroNode);
            System.out.println("修改后的链表情况:");
    
            // 显示链表
            singleLinkedList.list();
        }
    }
    
    // 定义 SingleLinkedList 管理英雄
    class SingleLinkedList{
        // 先初始化一个头结点,头结点,不存放具体的数据
        private HeroNode head=new HeroNode(0,"","");
    
        // 添加节点到单向链表
        // 2.  考虑编号顺序的情况,根据排名 将英雄插入到 指定位置 (如果有这个排名,则添加失败,并给出提示)
        //      思路:
        //          a. 通过辅助变量temp遍历找到新添加的节点的要添加的位置
        //          b. 新节点.next = temp.next;
        //              temp.next = 新节点
        public void  addByOrder(HeroNode heroNode){
            //  因为头节点不能动,通过一个辅助变量来帮组找到添加的位置
            //  因为单链表,因为找的 temp 是位于 添加位置的前一个节点,否则插入不了
            HeroNode temp=head;
            boolean flag= false;  // flag 标识添加的编号是否存在,默认为false
            while (true){
                if(temp.next==null){  //  说明 temp 已经在链表的最后
                    break;
                }
                if(temp.next.no> heroNode.no){  // 位置找到,就在 temp 的后面插入
                    break;
                }else if(temp.next.no == heroNode.no){  // 说明希望添加的 heroNode 的编号已经存在了
                    flag= true;   // 说明编号存在
                    break;
                }
                temp=temp.next;  // 后移,遍历当前链表
            }
    
            // 判断 flag 的值
            if(flag){   //  不能添加,说明编号存在
                System.out.printf("准备插入的英雄的编号 %d 已经存在了,不能加入\n",heroNode.no);
            }else{
                //  插入到链表中,temp的后面
                heroNode.next=temp.next;
                temp.next=heroNode;
            }
        }
    
    
        // 修改节点的信息,根据 no编号 来修改,即 no编号 不能改
        // 说明
        // 1. 根据 newHeroNode 的 no 来修改即可
        public  void update(HeroNode newHeroNode){
            //  判断是否空
            if(head.next == null){
                System.out.println("链表为空~");
                return;
            }
    
            // 找到需要修改的节点,根据 no 编号
            // 定义一个辅助变量
            HeroNode temp = head.next;
            boolean flag=false;  // 表示是否找到该节点
            while(true){
                if (temp==null){
                    break;  // 到链表的已经遍历完链表
                }
                if(temp.no==newHeroNode.no){
                    //  找到
                    flag=true;
                    break;
                }
                temp=temp.next;
            }
            //  根据 flag 判断是否找到要修改的节点
            if(flag){
                temp.name=newHeroNode.name;
                temp.nickname=newHeroNode.nickname;
            }else{   //  没有找到
                System.out.printf("没有找到 编号 %d 的节点,不能修改\n",newHeroNode.no);
            }
        }
    
        // 显示链表【遍历】
        public void list(){
            //  判断链表是否为空
            if(head.next == null){
                System.out.println("链表为空");
                return;
            }
    
            // 因为头节点不能动,因此需要一个辅助变量来遍历
            HeroNode temp = head.next;
            while (true){
                // 判断是否到链表最后
                if(temp == null){
                    break;
                }
                // 输出节点信息
                System.out.println(temp);
    
                // 将 temp 后移
                temp=temp.next;
            }
        }
    }
    
    
    // 定义HeroNode,每个 HeroNode 对象就是一个节点
    class HeroNode{
        public int no;  // 编号
        public String name;   // 名字
        public String nickname;  // 昵称
        public HeroNode next;  // 指向下一个节点
    
        // 构造器
        public HeroNode(int no,String name,String nickname){
            this.no=no;
            this.name=name;
            this.nickname=nickname;
        }
    
        // 为了显示方法,重写 toString
        @Override
        public  String toString(){
            return "HeroNode [ no = "+no+" , name = "+name+" , nickname = "+nickname+" ]";
        }
    }
    

    输出结果:

    HeroNode [ no = 1 , name = 宋江 , nickname = 及时雨 ]
    HeroNode [ no = 2 , name = 卢俊义 , nickname = 玉麒麟 ]
    HeroNode [ no = 3 , name = 吴用 , nickname = 智多星 ]
    HeroNode [ no = 4 , name = 林冲 , nickname = 豹子头 ]
    
    修改后的链表情况:
    HeroNode [ no = 1 , name = 宋江 , nickname = 及时雨 ]
    HeroNode [ no = 2 , name = 小卢 , nickname = 玉麒麟~ ]
    HeroNode [ no = 3 , name = 吴用 , nickname = 智多星 ]
    HeroNode [ no = 4 , name = 林冲 , nickname = 豹子头 ]
    
  4. 查看某个英雄(查找)

    借助辅助变量 temp 遍历链表查找即可.

1.1.3 面试题
  1. 求单链表中有效节点的个数

    public class SingleLinkedListDemo {
        public static void main(String[] args) {
            
            // 先创建节点
            HeroNode hero1=new HeroNode(1,"宋江","及时雨");
            HeroNode hero2=new HeroNode(2,"卢俊义","玉麒麟");
            HeroNode hero3=new HeroNode(3,"吴用","智多星");
            HeroNode hero4=new HeroNode(4,"林冲","豹子头");
    
            // 创建一个链表
            SingleLinkedList singleLinkedList=new SingleLinkedList();
    
            // 按编号的顺序添加英雄
            singleLinkedList.addByOrder(hero1);
            singleLinkedList.addByOrder(hero2);
            singleLinkedList.addByOrder(hero3);
            singleLinkedList.addByOrder(hero4);
    
            //  测试一下  求单链表中有效节点的个数
            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,"","");
    
        // 返回头节点
        public HeroNode getHead(){
            return head;
        }
    
        // 添加节点到单向链表
        // 2.  考虑编号顺序的情况,根据排名 将英雄插入到 指定位置 (如果有这个排名,则添加失败,并给出提示)
        //      思路:
        //          a. 通过辅助变量temp遍历找到新添加的节点的要添加的位置
        //          b. 新节点.next = temp.next;
        //              temp.next = 新节点
        public void  addByOrder(HeroNode heroNode){
            //  因为头节点不能动,通过一个辅助变量来帮组找到添加的位置
            //  因为单链表,因为找的 temp 是位于 添加位置的前一个节点,否则插入不了
            HeroNode temp=head;
            boolean flag= false;  // flag 标识添加的编号是否存在,默认为false
            while (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;
            }
        }
    }
    
    
    // 定义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;
        }
    }
    
  2. 查找单链表中的倒数第k个结点 【新浪面试题】

    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(hero2);
            singleLinkedList.addByOrder(hero3);
            singleLinkedList.addByOrder(hero4);
    
            // 显示链表
            singleLinkedList.list();
    
            //  测试一下  查找单链表中的倒数第k个结点
            HeroNode res=findLastIndexNode(singleLinkedList.getHead(),1);
            System.out.println();
            System.out.println("res="+res);
        }
    
        //  查找单链表中的倒数第k个结点【新浪面试题】
        //  思路:
        //    1.  编写一个方法:接收 head 节点,同时接收一个 index
        //    2.  index 表示是倒数第 index 个节点
        //    3.  先把链表从头到尾遍历,得到链表的总的长度
        //    4.  得到 size 后,从链表的第一个开始遍历(size-index)个
        //    5.  如果找到了,则返回该节点,否则返回 null
        public static HeroNode findLastIndexNode(HeroNode head,int index){
            //   判断如果链表为空,则返回null
            if(head.next==null){
                return null;  //  没有找到
            }
            //  第一个遍历得到链表的长度(节点个数)
            int size =getLength(head);
            // 第二次遍历 size-index 位置,就是倒数的第 K 个节点
            // 先做一个index的校验
            if(index<=0|| index>size){
                return null;
            }
            // 定义一个辅助变量,for循环定位到倒数的index
            HeroNode cur = head.next;
            for(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,"","");
    
        // 返回头节点
        public HeroNode getHead(){
            return head;
        }
    
        // 添加节点到单向链表
        // 2.  考虑编号顺序的情况,根据排名 将英雄插入到 指定位置 (如果有这个排名,则添加失败,并给出提示)
        //      思路:
        //          a. 通过辅助变量temp遍历找到新添加的节点的要添加的位置
        //          b. 新节点.next = temp.next;
        //              temp.next = 新节点
        public void  addByOrder(HeroNode heroNode){
            //  因为头节点不能动,通过一个辅助变量来帮组找到添加的位置
            //  因为单链表,因为找的 temp 是位于 添加位置的前一个节点,否则插入不了
            HeroNode temp=head;
            boolean flag= false;  // flag 标识添加的编号是否存在,默认为false
            while (true){
                if(temp.next==null){  //  说明 temp 已经在链表的最后
                    break;
                }
                if(temp.next.no> heroNode.no){  // 位置找到,就在 temp 的后面插入
                    break;
                }else if(temp.next.no == heroNode.no){  // 说明希望添加的 heroNode 的编号已经存在了
                    flag= true;   // 说明编号存在
                    break;
                }
                temp=temp.next;  // 后移,遍历当前链表
            }
    
            // 判断 flag 的值
            if(flag){   //  不能添加,说明编号存在
                System.out.printf("准备插入的英雄的编号 %d 已经存在了,不能加入\n",heroNode.no);
            }else{
                //  插入到链表中,temp的后面
                heroNode.next=temp.next;
                temp.next=heroNode;
            }
        }
    
        // 显示链表【遍历】
        public void list(){
            //  判断链表是否为空
            if(head.next == null){
                System.out.println("链表为空");
                return;
            }
    
            // 因为头节点不能动,因此需要一个辅助变量来遍历
            HeroNode temp = head.next;
            while (true){
                // 判断是否到链表最后
                if(temp == null){
                    break;
                }
                // 输出节点信息
                System.out.println(temp);
    
                // 将 temp 后移
                temp=temp.next;
            }
        }
    }
    
    
    // 定义HeroNode,每个 HeroNode 对象就是一个节点
    class HeroNode{
        public int no;  // 编号
        public String name;   // 名字
        public String nickname;  // 昵称
        public HeroNode next;  // 指向下一个节点
    
        // 构造器
        public HeroNode(int no,String name,String nickname){
            this.no=no;
            this.name=name;
            this.nickname=nickname;
        }
    
        // 为了显示方法,重写 toString
        @Override
        public  String toString(){
            return "HeroNode [ no = "+no+" , name = "+name+" , nickname = "+nickname+" ]";
        }
    }
    
  3. 单链表的反转【腾讯面试题,有点难度】

    图解:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8dgOvpqX-1602656421593)(数据结构与算法img/单链表的反转.png)]

    具体代码:

    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(hero2);
            singleLinkedList.addByOrder(hero3);
            singleLinkedList.addByOrder(hero4);
    
            //  测试一下  单链表的反转
            System.out.println("原来链表的情况~~~");
            singleLinkedList.list();
    
            System.out.println();
            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连接到新链表上
                cur=next;  // 让cur后移
            }
            // 将 head.next 指向 reverseHead.next ,实现单链表的反转
            head.next=reverseHead.next;
        }
    
    // 定义 SingleLinkedList 管理英雄
    class SingleLinkedList{
        // 先初始化一个头结点,头结点,不存放具体的数据
        private HeroNode head=new HeroNode(0,"","");
    
        // 返回头节点
        public HeroNode getHead(){
            return head;
        }
    
        // 添加节点到单向链表
        // 2.  考虑编号顺序的情况,根据排名 将英雄插入到 指定位置 (如果有这个排名,则添加失败,并给出提示)
        //      思路:
        //          a. 通过辅助变量temp遍历找到新添加的节点的要添加的位置
        //          b. 新节点.next = temp.next;
        //              temp.next = 新节点
        public void  addByOrder(HeroNode heroNode){
            //  因为头节点不能动,通过一个辅助变量来帮组找到添加的位置
            //  因为单链表,因为找的 temp 是位于 添加位置的前一个节点,否则插入不了
            HeroNode temp=head;
            boolean flag= false;  // flag 标识添加的编号是否存在,默认为false
            while (true){
                if(temp.next==null){  //  说明 temp 已经在链表的最后
                    break;
                }
                if(temp.next.no> heroNode.no){  // 位置找到,就在 temp 的后面插入
                    break;
                }else if(temp.next.no == heroNode.no){  // 说明希望添加的 heroNode 的编号已经存在了
                    flag= true;   // 说明编号存在
                    break;
                }
                temp=temp.next;  // 后移,遍历当前链表
            }
    
            // 判断 flag 的值
            if(flag){   //  不能添加,说明编号存在
                System.out.printf("准备插入的英雄的编号 %d 已经存在了,不能加入\n",heroNode.no);
            }else{
                //  插入到链表中,temp的后面
                heroNode.next=temp.next;
                temp.next=heroNode;
            }
        }
    
        // 显示链表【遍历】
        public void list(){
            //  判断链表是否为空
            if(head.next == null){
                System.out.println("链表为空");
                return;
            }
    
            // 因为头节点不能动,因此需要一个辅助变量来遍历
            HeroNode temp = head.next;
            while (true){
                // 判断是否到链表最后
                if(temp == null){
                    break;
                }
                // 输出节点信息
                System.out.println(temp);
    
                // 将 temp 后移
                temp=temp.next;
            }
        }
    }
    
    // 定义HeroNode,每个 HeroNode 对象就是一个节点
    class HeroNode{
        public int no;  // 编号
        public String name;   // 名字
        public String nickname;  // 昵称
        public HeroNode next;  // 指向下一个节点
    
        // 构造器
        public HeroNode(int no,String name,String nickname){
            this.no=no;
            this.name=name;
            this.nickname=nickname;
        }
    
        // 为了显示方法,重写 toString
        @Override
        public  String toString(){
            return "HeroNode [ no = "+no+" , name = "+name+" , nickname = "+nickname+" ]";
        }
    }
    
  4. 从尾到头打印单链表 【百度,要求方式1:反向遍历 。 方式2:Stack栈】

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

    具体代码:

    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(hero2);
            singleLinkedList.addByOrder(hero3);
            singleLinkedList.addByOrder(hero4);
    
            System.out.println("原来链表的情况~~~");
            singleLinkedList.list();
    
            //  测试 逆序打印单链表
            System.out.println();
            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;  //  cur后移,这样就可以压入下一个节点
            }
    
            //  将栈中的节点进行打印,pop出栈
            while (stack.size()>0){
                System.out.println(stack.pop());   //  stack的特点:先进后出
            }
        }
        
    
    // 定义 SingleLinkedList 管理英雄
    class SingleLinkedList{
        // 先初始化一个头结点,头结点,不存放具体的数据
        private HeroNode head=new HeroNode(0,"","");
    
        // 返回头节点
        public HeroNode getHead(){
            return head;
        }
    
        // 添加节点到单向链表
        // 2.  考虑编号顺序的情况,根据排名 将英雄插入到 指定位置 (如果有这个排名,则添加失败,并给出提示)
        //      思路:
        //          a. 通过辅助变量temp遍历找到新添加的节点的要添加的位置
        //          b. 新节点.next = temp.next;
        //              temp.next = 新节点
        public void  addByOrder(HeroNode heroNode){
            //  因为头节点不能动,通过一个辅助变量来帮组找到添加的位置
            //  因为单链表,因为找的 temp 是位于 添加位置的前一个节点,否则插入不了
            HeroNode temp=head;
            boolean flag= false;  // flag 标识添加的编号是否存在,默认为false
            while (true){
                if(temp.next==null){  //  说明 temp 已经在链表的最后
                    break;
                }
                if(temp.next.no> heroNode.no){  // 位置找到,就在 temp 的后面插入
                    break;
                }else if(temp.next.no == heroNode.no){  // 说明希望添加的 heroNode 的编号已经存在了
                    flag= true;   // 说明编号存在
                    break;
                }
                temp=temp.next;  // 后移,遍历当前链表
            }
    
            // 判断 flag 的值
            if(flag){   //  不能添加,说明编号存在
                System.out.printf("准备插入的英雄的编号 %d 已经存在了,不能加入\n",heroNode.no);
            }else{
                //  插入到链表中,temp的后面
                heroNode.next=temp.next;
                temp.next=heroNode;
            }
        }
    
        // 显示链表【遍历】
        public void list(){
            //  判断链表是否为空
            if(head.next == null){
                System.out.println("链表为空");
                return;
            }
    
            // 因为头节点不能动,因此需要一个辅助变量来遍历
            HeroNode temp = head.next;
            while (true){
                // 判断是否到链表最后
                if(temp == null){
                    break;
                }
                // 输出节点信息
                System.out.println(temp);
    
                // 将 temp 后移
                temp=temp.next;
            }
        }
    }
    
    // 定义HeroNode,每个 HeroNode 对象就是一个节点
    class HeroNode{
        public int no;  // 编号
        public String name;   // 名字
        public String nickname;  // 昵称
        public HeroNode next;  // 指向下一个节点
    
        // 构造器
        public HeroNode(int no,String name,String nickname){
            this.no=no;
            this.name=name;
            this.nickname=nickname;
        }
    
        // 为了显示方法,重写 toString
        @Override
        public  String toString(){
            return "HeroNode [ no = "+no+" , name = "+name+" , nickname = "+nickname+" ]";
        }
    }
    
  5. 合并两个有序的单链表,合并之后的链表依然有序

1.2 双向链表

双向链表:每一个节点除了拥有data和next指针, 还拥有指向前置节点的prev指针。

结构图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OV5H0d0N-1602656421596)(数据结构与算法img/双向链表.png)]

  • 单向链表和双向链表的比较:

    1. 单向链表:查找的方向只能是一个方向

      双向链表:可以向前或者向后查找;

    2. 单向链表:不能自我删除,需要靠辅助节点 ;

      双向链表:可以自我删除

  • 双向链表的操作:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7HdYloBN-1602656421598)(数据结构与算法img/双向链表1.png)]

    1. 遍历:和 单链表一样,但其可以向前,也可以向后查找;

    2. 添加 (默认添加到双向链表的最后)

      • 先找到双向链表的最后这个节点,比如temp

      • temp.next = newHeroNode;

      • newHeroNode.pre = temp;

    3. 修改 :和 单向链表一样;

    4. 删除

      • 因为是双向链表,因此可以实现 自我删除某个节点
      • 直接找到要删除的这个节点,比如temp
      • temp.pre.next = temp.next;
      • temp.next.pre = temp.pre;

具体代码:

package roselin.linkedlist;

public class DoubleLinkedListDemo {
    public static void main(String[] args) {
        System.out.println("双向链表的测试:");

        // 先创建节点
        HeroNode1 hero1=new HeroNode1(1,"宋江","及时雨");
        HeroNode1 hero2=new HeroNode1(2,"卢俊义","玉麒麟");
        HeroNode1 hero3=new HeroNode1(3,"吴用","智多星");
        HeroNode1 hero4=new HeroNode1(4,"林冲","豹子头");

        // 创建一个链表
        DoubleLinkedList doubleLinkedList=new DoubleLinkedList();
        // 添加
        doubleLinkedList.add(hero1);
        doubleLinkedList.add(hero2);
        doubleLinkedList.add(hero3);
        doubleLinkedList.add(hero4);

        // 遍历
        doubleLinkedList.list();

        // 修改
        HeroNode1 newHeroNode=new HeroNode1(4,"公孙胜","入云龙");
        doubleLinkedList.update(newHeroNode);
        System.out.println();
        System.out.println("修改后:");
        doubleLinkedList.list();

        // 删除
        doubleLinkedList.del(2);
        System.out.println();
        System.out.println("删除后:");
        doubleLinkedList.list();
    }
}

//  创建一个双向链表的类
class DoubleLinkedList {
    // 先初始化一个头结点,头结点,不存放具体的数据
    private HeroNode1 head = new HeroNode1(0, "", "");

    // 返回头节点
    public HeroNode1 getHead() {
        return head;
    }


    // 遍历双向链表的方法(和单链表一样)
    // 显示链表【遍历】
    public void list() {
        //  判断链表是否为空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }

        // 因为头节点不能动,因此需要一个辅助变量来遍历
        HeroNode1 temp = head.next;
        while (true) {
            // 判断是否到链表最后
            if (temp == null) {
                break;
            }
            // 输出节点信息
            System.out.println(temp);

            // 将 temp 后移
            temp = temp.next;
        }
    }

    // 添加一个节点到双向链表的最后
    public void add(HeroNode1 heroNode1) {
        // 因为 head 节点不能动,因此需要一个辅助变量 temp
        HeroNode1 temp = head;

        // 遍历链表,找到链表最后节点
        while (true) {
            //  找到链表最后节点
            if (temp.next == null) {
                break;
            }

            // 如果没有找到最后节点,就将 temp 后移
            temp = temp.next;
        }

        // 当退出 while 循环时,temp就指向了链表的最后节点
        // 形成一个双向链表
        temp.next = heroNode1;
        heroNode1.pre = temp;
    }

    // 修改一个节点
    public  void update(HeroNode1 newHeroNode){
        //  判断是否空
        if(head.next == null){
            System.out.println("链表为空~");
            return;
        }

        // 找到需要修改的节点,根据 no 编号
        // 定义一个辅助变量
        HeroNode1 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.  对于双向链表,直接找到要删除的这个节点
    //      2.  找到后,自我删除即可
    public  void del(int no){
        //  判断当前链表是否为空
        if(head.next == null){   // 空链表
            System.out.println("链表为空,无法删除");
            return;
        }


        HeroNode1 temp =head.next;
        boolean flag = false;   // 标识是否 找到待删除节点
        while (true){
            if(temp==null){
                break;
            }
            if(temp.no==no){
                //  找到的待删除节点的前一个节点 temp
                flag=true;
                break;
            }
            temp=temp.next;    // temp后移,遍历当前链表
        }
        // 判断 flag
        if(flag){   // 找到
            //  可以删除
//            temp.next=temp.next.next;   // 单向链表
            temp.pre.next=temp.next;

            //  如果是最后一个节点,就不需要执行这句话   temp.next.pre=temp.pre; ,否则会出现空指针异常
            if(temp.next!=null){
                temp.next.pre=temp.pre;
            }
        }else{
            System.out.printf("要删除的 %d 节点不存在\n",no);
        }
    }
}

// 定义HeroNode1,每个 HeroNode 对象就是一个节点
class HeroNode1 {
    public int no;  // 编号
    public String name;   // 名字
    public String nickname;  // 昵称
    public HeroNode1 next;  // 指向下一个节点,默认为null
    public HeroNode1 pre;    // 指向前一个节点,默认为null

    // 构造器
    public HeroNode1(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 + " ]";
    }
}

拓展:

双向链表的第二种添加方式,按照编号顺序(按照单链表的顺序添加,稍作修改即可).

★ 数组 VS 链表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9sA69PJ2-1602656421598)(数据结构与算法img/数组VS链表.png)]

  • 从表格可以看出, 数组的优势在于能够快速定位元素,对于读操作多、 写操作少的场景来说, 用数组更合适一些。
  • 链表的优势在于能够灵活地进行插入和删除操作, 如果需要在尾部频繁插入、 删除元素, 用链表更合适一些。

1.3 单向环形链表

Josephu(约瑟夫、约瑟夫环) 问题

设编号为1,2,… n 的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列

提示
用一个不带头结点的循环链表来处理Josephu 问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束。

举例:

  • n = 5 , 即有5个人 ;

  • k = 1, 从第一个人开始报数;

  • m = 2, 数2下;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pM6wohjB-1602656421600)(数据结构与算法img/单向环形链表.png)]

具体代码:

public class Josephu {
    public static void main(String[] args) {
        CircleSingleLinkedList circleSingleLinkedList=new CircleSingleLinkedList();
        circleSingleLinkedList.addBoy(5);  // 加入5个小孩节点
        circleSingleLinkedList.showBoy();

        circleSingleLinkedList.countBoy(1,2,5);
    }
}

//  创建一个环形的单向链表
class CircleSingleLinkedList{
    //  创建一个first节点,当前没有编号
    private Boy first=null;

    //  添加小孩节点,构建一个环形的链表
    public void addBoy(int nums){
        //  nums 做一个数据校验
        if(nums<1){
            System.out.println("nums 的值不正确");
            return;
        }

        Boy curBoy=null;  // 辅助指针,帮助构建环形链表

        // 使用for循环来创建环形链表
        for(int i =1; i<=nums;i++){
            //  根据编号,创建小孩节点
            Boy boy=new Boy(i);

            //  如果是第一个小孩
            if(i==1){
                first=boy;
                first.setNext(first);
                curBoy=first;  // 让 curBoy指向第一个小孩
            }else{
                curBoy.setNext(boy);
                boy.setNext(first);
                curBoy=boy;
            }
        }
    }

    // 遍历当前 环形链表
    public void showBoy(){
        //  判断链表是否为空
        if(first==null){
            System.out.println("没有任何小孩~~");
            return;
        }
        //  因为 first不能动,因此仍然使用一个辅助指针完成遍历
        Boy curBoy=first;
        while (true){
            System.out.printf("小孩的编号 %d  \n",curBoy.getNo());
            if(curBoy.getNext()==first){    //  说明已经遍历完毕
                break;
            }
            curBoy =curBoy.getNext();   //  curBoy后移
        }
    }

    //  根据用户的输入,计算小孩出圈的顺序
    /**
     *
     * @param startNo  表示从第几个小孩开始数数
     * @param countNum  表示数几下
     * @param nums    表示最初有几个小孩在圈中
     */
    public void countBoy(int startNo,int countNum,int nums){
        //  先对数据进行校验
        if(first==null  || startNo<1||startNo>nums){
            System.out.println("参数输入有误,请重新输入~~~");
            return;
        }
        //  创建辅助变量 helper,帮助完成小孩出圈
        Boy helper =first;
        //  helper变量事先应该指向环形链表的最后这个节点
        while (true){
            if(helper.getNext()==first){  // 说明helper指向最后节点
                break;
            }
            helper=helper.getNext();
        }
        //  小孩报数前,先让 first 和 helper 移动k-1次
        for(int j=0;j<startNo-1;j++){
            first=first.getNext();
            helper=helper.getNext();
        }

        // 当小孩报数时,让first和helper指针同时 的移动 m-1次,然后出圈
        // 这里是一个循环操作,知道圈中只有一个节点
        while (true){
            if(helper==first){   //  说明圈中只有一个节点
                break;
            }
            //  让 first 和 helper 指针同时 的移动 counNum-1,
            for (int j = 0;j<countNum-1;j++){
                first=first.getNext();
                helper=helper.getNext();
            }
            // 这时first 指向的节点就是要出圈的小孩节点
            System.out.printf("小孩%d 出圈\n",first.getNo());
            // 这时将 first 指向的小孩节点出圈
            first=first.getNext();
            helper.setNext(first);
        }
        System.out.printf("最后留在圈中的小孩编号%d \n",helper.getNo());
    }
}


// 创建一个 Boy类,表示一个节点
class Boy{
    private int no;  //编号
    private Boy next;  //  指向下一个节点,默认null
    public Boy(int no){
        this.no=no;
    }
    public int getNo(){
        return no;
    }
    public void setNo(int no){
        this.no=no;
    }
    public Boy getNext() {
        return next;
    }
    public void setNext(Boy next) {
        this.next = next;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值