数据结构与算法(第三章 链表)

3.1链表(Linked List)介绍

后面实现链表需先创建节点类,然后在创建链表

链表是有序的列表,但是它在内存中存储如下:
在这里插入图片描述
小结上图:

  1. 链表是以节点的方式来存储,是链式存储
  2. 每个节点包含data域,next域:指向下一个节点
  3. 如图:发现链表的各个节点不一定是连续存储
  4. 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定

@单链表(带头结点)逻辑结构示意图如下
在这里插入图片描述

3.2单链表的应用实例

使用带head头的单向链表实现 -水浒英雄排行榜管理完成对英雄人物的增删改查操作。
(1)第一种方法在添加英雄时,直接添加到链表的尾部

思路分析示意图:
在这里插入图片描述
(2)第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)

思路分析示意图:

在这里插入图片描述
(3)修改节点功能
思路1.先找到该节点(通过遍历)2.temp.name=newHeroNode.name;temp.nickname=newHeroNode.nickname
(4)删除节点

思路分析示意图:
在这里插入图片描述
(5)实现代码演示:

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, "林冲", "豹子头");
        HeroNode hero5 = new HeroNode(3, "孙悟空", "美猴王");

        //创建链表
        SingleHeroNode singleHeroNode = new SingleHeroNode();
//        singleHeroNode.add(hero1);
//        singleHeroNode.add(hero2);
//        singleHeroNode.add(hero3);
//        singleHeroNode.add(hero4);
        singleHeroNode.addByOrder(hero2);
        singleHeroNode.addByOrder(hero1);
        singleHeroNode.addByOrder(hero3);
        singleHeroNode.addByOrder(hero4);
        //singleHeroNode.addByOrder(hero5);
        singleHeroNode.update(hero5);
        singleHeroNode.delete(1);
        singleHeroNode.delete(2);
        singleHeroNode.delete(3);
        singleHeroNode.delete(4);

        //展示
        singleHeroNode.list();
    }
}

//创建单链表
class SingleHeroNode{
    //首先定义头结点,头结点用来标记不可移动,所以下面遍历都需重新定义变量temp
    private HeroNode head=new HeroNode(0,"","");

    //添加节点方式一:不考虑顺序
    //1.首先定义一个临时变量temp来遍历链表(节点需要添加到末尾遍历出next为null的节点)
    //2.将节点添加
    public void add(HeroNode node){
        HeroNode temp=head;
        //一直循环遍历
        while (true){
            if (temp.next==null){
                break;
            }
            //如果没有找到,则将此指针后移
            temp=temp.next;
        }
        temp.next=node;
    }

    //添加节点方式二:考虑节点的顺序(按照节点的编号进行添加)
    //如果编号已经存在,则提示添加失败
    public void addByOrder(HeroNode node){
        //因为头结点不可移动,所以需定义变量temp来进行遍历
        HeroNode temp=head;
        boolean flag=false; //定义标识符来标识添加的英雄编号是否存在
        //遍历找到加入节点位置的前一个位置
        while (true){
            if (temp.next==null){
                break;  //遍历到节点末尾
            }
            if (temp.next.no>node.no){
                break;    //说明加入节点位置的前一个节点找到
            }else if (temp.next.no==node.no){
                flag=true;
                break;     //加入英雄的编号已经存在,添加失败
            }
            temp=temp.next;  //以上条件不满足,指针下移
        }
        if (flag){
            System.out.printf("添加的英雄编号%d已经存在,添加失败\n",node.no);
        }else {
            node.next=temp.next;
            temp.next=node;
        }
    }

    //修改英雄信息(根据英雄的no修改英雄的名字和别名)
    public void update(HeroNode newHerNode){
        //定义变量temp方便遍历
        HeroNode temp=head;
        boolean falg=false;  //标识找到指定的英雄节点
        while (true){
            if (temp.next==null){
                break;    //遍历到结尾,没有此结点
            }
            if (temp.next.no==newHerNode.no){
                falg=true;    //标识已经找到
                break;
            }
            temp=temp.next;
        }
        if (falg){
            temp.next.name=newHerNode.name;
            temp.next.nickName=newHerNode.nickName;
        }else {
            System.out.printf("没有%号的英雄节点,修改失败\n",newHerNode.no);
        }
    }

    //删除节点(遍历找到待删除节点的前一个节点)
    public void delete(int no){
        HeroNode temp=head;
        boolean flag=false;//标识是否找到待删除节点
        while (true){
            if (temp.next==null){
                break;  //已经遍历到尾部,退出循环
            }
            if (temp.next.no==no){
                flag=true;
                break;  //标识找到待删除的节点
            }
            temp=temp.next;   //不满足条件后移
        }
        if (flag){
            temp.next=temp.next.next;  //删除元素
        }else {
            System.out.printf("没有%d号英雄节点,删除失败\n",no);
        }
    }

    //遍历节点
    public void list(){
        HeroNode temp=head;
        if (temp.next==null){
            System.out.println("单链表为空");
        }
        while (true){
            if (temp.next==null){
                break;
            }
            System.out.println(temp.next); //注意此处temp定义的头结点,所以打印temp.next
            temp=temp.next;
        }
    }
}

//创建类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方法(因为此类中有next属性,为了防止重复打印后面的节点,所以省略next)


    @Override
    public String toString() {
        return "HeroNode{" + "no=" + no + ", name='" + name + '\'' + ", nickName='" + nickName + '\'' + '}';
    }
}

单链表*面试题(新浪、百度、腾讯)
单链表的常见面试题有如下:
(1)求单链表中有效节点的个数
(2)查找单链表中的倒数第K个节点【新浪面试题】
(3)单链表的反转【腾讯面试题,有点难度】
(4)从尾到头打印单链表【百度,要求方式1;反向遍历。方式2:Stack栈】

代码:为了方便书写和观看,上述的题目直接写在实现单链表的上面

package com.itcast.linkedList;

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, "林冲", "豹子头");
        HeroNode hero5 = new HeroNode(3, "孙悟空", "美猴王");

        //创建链表
        SingleHeroNode singleHeroNode = new SingleHeroNode();
//        singleHeroNode.add(hero1);
//        singleHeroNode.add(hero2);
//        singleHeroNode.add(hero3);
//        singleHeroNode.add(hero4);
        singleHeroNode.addByOrder(hero2);
        singleHeroNode.addByOrder(hero1);
        singleHeroNode.addByOrder(hero3);
        singleHeroNode.addByOrder(hero4);
        //singleHeroNode.addByOrder(hero5);
        //singleHeroNode.update(hero5);

        //展示
        singleHeroNode.list();

        //reversePrint(singleHeroNode.getHead());

        //reverse(singleHeroNode.getHead());
        //singleHeroNode.list();
        //System.out.println(findLastNode(singleHeroNode.getHead(), 5));

    }
    /**
     * 求单链表中有几个有效节点(面试题)(如果单链表有头节点,删除头节点)
     * @param heroNode 参数:头节点
     * @return 返回有效节点个数
     */
    public static int getlength(HeroNode heroNode){
        if (heroNode.next==null){
            return 0;   //没有有效节点
        }
        int length=0;
        HeroNode node=heroNode;
        while (node.next!=null){
            length++;
            node=node.next;
        }
        return length;
    }

    //查找单链表中的倒数第k个节点【新浪面试题】
    //思路:
    //1.编写一个方法,接收head节点,同时接收一个index(index含义是倒数第index个节点)
    //2.第一次遍历循环出此链表一共有几个有效节点size
    //3.第二次遍历出(size-index)的节点
    /**
     * 查找单链表中的倒数第k个节点【新浪面试题】
     * @param heroNode  传入单链表的头结点
     * @param index   倒数第几个节点
     * @return
     */
    public static HeroNode findLastNode(HeroNode heroNode,int index){
        //判断节点是否为空
        if (heroNode.next==null){
            return null;
        }
        //1.遍历一共有几个节点(调用上面的方法)
        int size = getlength(heroNode);
        //2.定义临时变量
        HeroNode temp=heroNode.next;
        //校验index是否合格
        if (index <=0 || index >size){
            return null;    //输入index不合格
        }
        for (int i=0;i<size-index;i++){ //3=1+2
            temp=temp.next;
        }
        return temp;
    }

    //思路:
    //1.先定义一个节点reverseHead=new HeroNode();
    //2.从头到尾遍历原来的链表,没遍历一个节点,就将其取出,并放在新的链表reverseHead的最前端
    //3.原来的链表的head.next=reverseHead.next
    /**
     * 单链表的反转【腾讯面试题】
     * @param headNode
     */
    public static void reverse(HeroNode headNode){
        //如果结点为空或者只有一个节点,则原样返回
        if (headNode.next==null || headNode.next.next==null){
            return;
        }
        //定义临时变量temp方便遍历
        HeroNode temp=headNode.next;
        HeroNode next=null;   //存放正在遍历节点的下一个节点(后面需要用)
        HeroNode curNode=new HeroNode(0,"","");
        //遍历
        while (temp!=null){
            next=temp.next;   //首先存放好下一个节点
            temp.next=curNode.next;   //将遍历原始链表的节点的下一个节点指向新节点curNode的下一个节点
            curNode.next=temp;    //将新节点curNode的下一个节点指向正在遍历的节点temp
                                 //以上两步是将正在遍历的节点加入到新节点头部(逻辑看着复杂,画图就很清晰可看出)
            temp=next;          //注意这里不可使用temp=temp.next,因为此时的temp.next的指向已经变动了,指向了新链表
        }
        headNode.next=curNode.next;     //给原始链表反转
    }

    //从尾到头打印单链表【百度面试题】
    //方式一:先将单链表进行反转操作,然后再遍历即可,这样做的问题会破坏原来单链表的结构,不建议
    //方式二:可以利用栈这个数据结构,将各个节点压入到栈中,然后利用栈的先进后出特点来实现逆序打印的效果
    /**
     * 从尾到头打印单链表【百度面试题】
     * @param heroNode
     */
    public static void reversePrint(HeroNode heroNode){
        if (heroNode.next==null){
            return;    //链表为空
        }
        //创建一个栈的实例
        Stack<HeroNode> stack = new Stack<>();
        HeroNode temp = heroNode.next;
        //对链表的数据进行遍历然后压栈处理
        while (temp!=null){
            stack.push(temp);
            temp=temp.next;
        }
        //循环遍历出栈
        while (stack.size()>0){
            System.out.println(stack.pop());
        }
    }
}

//创建单链表
class SingleHeroNode{
    //首先定义头结点,头结点用来标记不可移动,所以下面遍历都需重新定义变量temp
    private HeroNode head=new HeroNode(0,"","");

    //获取头节点
    public HeroNode getHead() {
        return head;
    }
    //添加节点方式一:不考虑顺序
    //1.首先定义一个临时变量temp来遍历链表(节点需要添加到末尾遍历出next为null的节点)
    //2.将节点添加
    public void add(HeroNode node){
        HeroNode temp=head;
        //一直循环遍历
        while (true){
            if (temp.next==null){
                break;
            }
            //如果没有找到,则将此指针后移
            temp=temp.next;
        }
        temp.next=node;
    }

    //添加节点方式二:考虑节点的顺序(按照节点的编号进行添加)
    //如果编号已经存在,则提示添加失败
    public void addByOrder(HeroNode node){
        //因为头结点不可移动,所以需定义变量temp来进行遍历
        HeroNode temp=head;
        boolean flag=false; //定义标识符来标识添加的英雄编号是否存在
        //遍历找到加入节点位置的前一个位置
        while (true){
            if (temp.next==null){
                break;  //遍历到节点末尾
            }
            if (temp.next.no>node.no){
                break;    //说明加入节点位置的前一个节点找到
            }else if (temp.next.no==node.no){
                flag=true;
                break;     //加入英雄的编号已经存在,添加失败
            }
            temp=temp.next;  //以上条件不满足,指针下移
        }
        if (flag){
            System.out.printf("添加的英雄编号%d已经存在,添加失败\n",node.no);
        }else {
            node.next=temp.next;
            temp.next=node;
        }
    }

    //修改英雄信息(根据英雄的no修改英雄的名字和别名)
    public void update(HeroNode newHerNode){
        //定义变量temp方便遍历
        HeroNode temp=head;
        boolean falg=false;  //标识找到指定的英雄节点
        while (true){
            if (temp.next==null){
                break;    //遍历到结尾,没有此结点
            }
            if (temp.next.no==newHerNode.no){
                falg=true;    //标识已经找到
                break;
            }
            temp=temp.next;
        }
        if (falg){
            temp.next.name=newHerNode.name;
            temp.next.nickName=newHerNode.nickName;
        }else {
            System.out.printf("没有%号的英雄节点,修改失败\n",newHerNode.no);
        }
    }

    //删除节点(遍历找到待删除节点的前一个节点)
    public void delete(int no){
        HeroNode temp=head;
        boolean flag=false;//标识是否找到待删除节点
        while (true){
            if (temp.next==null){
                break;  //已经遍历到尾部,退出循环
            }
            if (temp.next.no==no){
                flag=true;
                break;  //标识找到待删除的节点
            }
            temp=temp.next;   //不满足条件后移
        }
        if (flag){
            temp.next=temp.next.next;  //删除元素
        }else {
            System.out.printf("没有%d号英雄节点,删除失败\n",no);
        }
    }

    //遍历节点
    public void list(){
        HeroNode temp=head;
        if (temp.next==null){
            System.out.println("单链表为空");
        }
        while (true){
            if (temp.next==null){
                break;
            }
            System.out.println(temp.next); //注意此处temp定义的头结点,所以打印temp.next
            temp=temp.next;
        }
    }
}

//创建类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方法(因为此类中有next属性,为了防止重复打印后面的节点,所以省略next)


    @Override
    public String toString() {
        return "HeroNode{" + "no=" + no + ", name='" + name + '\'' + ", nickName='" + nickName + '\'' + '}';
    }
}

3.3双向链表应用实例

3.3.1双向链表的操作分析和实现

使用带head头的双向链表实现 -水浒英雄排行榜
@管理单向链表的缺点分析:
(1)单向链表,查找的方向只能是一个方向,而双向链表可以向前或者向后查找
(2)单向链表不能自我删除,需要靠辅助节点,而双向链表,则可以自我删除。
(3)分析了双向链表如何完成遍历,添加,修改和删除的思路

在这里插入图片描述
对上图的说明:
分析 双向链表的遍历,添加,修改,删除的操作思路===》代码实现

  1. 遍历 方和 单链表一样,只是可以向前,也可以向后查找
  2. 添加 (默认添加到双向链表的最后)
    (1) 先找到双向链表的最后这个节点
    (2) temp.next = newHeroNode
    (3) newHeroNode.pre = temp;
  3. 修改 思路和 原来的单向链表一样. 4) 删除
    (1) 因为是双向链表,因此,我们可以实现自我删除某个节点
    (2) 直接找到要删除的这个节点,比如 temp
    (3) temp.pre.next = temp.next
    (4) temp.next.pre = temp.pre;

@代码链表的代码实现

public class DoubleLinkedListDemo {
    public static void main(String[] args) {
       //测试双向链表
        //先创建节点
        HeroNode2 hero1 = new HeroNode2(1, "宋江", "及时雨");
        HeroNode2 hero2 = new HeroNode2(2, "卢俊义", "玉麒麟");
        HeroNode2 hero3 = new HeroNode2(3, "吴用", "智多星");
        HeroNode2 hero4 = new HeroNode2(4, "林冲", "豹子头");
        //创建一个双向链表
        DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
        doubleLinkedList.add(hero1);
        doubleLinkedList.add(hero2);
        doubleLinkedList.add(hero3);
        doubleLinkedList.add(hero4);
        doubleLinkedList.list();
        //修改
        HeroNode2 heroNode2 = new HeroNode2(4, "孙悟空", "斗阵神佛");
        doubleLinkedList.update(heroNode2);
        System.out.println("修改后的链表情况");
        doubleLinkedList.list();
        //删除
        doubleLinkedList.delete(2);
        System.out.println("删除后的链表情况");
        doubleLinkedList.list();
    }
}

//创建双向链表
class DoubleLinkedList{
    //首先定义头结点,头结点用来标记不可移动,所以下面遍历都需重新定义变量temp
    private HeroNode2 head=new HeroNode2(0,"","");

    //获取头节点
    public HeroNode2 getHead() {
        return head;
    }

    //遍历(与单向链表一样)
    public void list(){
        HeroNode2 temp=head;
        if (temp.next==null){
            System.out.println("单链表为空");
        }
        while (true){
            if (temp.next==null){
                break;
            }
            System.out.println(temp.next); //注意此处temp定义的头结点,所以打印temp.next
            temp=temp.next;
        }
    }
    //添加(在双向链表的末尾添加)
    public void add(HeroNode2 node){
        HeroNode2 temp=head;
        //一直循环遍历
        while (true){
            if (temp.next==null){
                break;
            }
            //如果没有找到,则将此指针后移
            temp=temp.next;
        }
        //添加到末尾
        temp.next=node;
        node.pre=temp;
    }
    //修改(与单向链表一样)
    public void update(HeroNode2 newHerNode){
        //定义变量temp方便遍历
        HeroNode2 temp=head;
        boolean falg=false;  //标识找到指定的英雄节点
        while (true){
            if (temp.next==null){
                break;    //遍历到结尾,没有此结点
            }
            if (temp.next.no==newHerNode.no){
                falg=true;    //标识已经找到
                break;
            }
            temp=temp.next;
        }
        if (falg){
            temp.next.name=newHerNode.name;
            temp.next.nickName=newHerNode.nickName;
        }else {
            System.out.printf("没有%号的英雄节点,修改失败\n",newHerNode.no);
        }
    }
    //删除
    public void delete(int no){
        HeroNode2 temp=head.next;
        boolean flag=false;//标识是否找到待删除节点
        while (true){
            if (temp==null){
                break;  //已经遍历到尾部,退出循环
            }
            if (temp.no==no){
                flag=true;
                break;  //标识找到待删除的节点
            }
            temp=temp.next;   //不满足条件后移
        }
        if (flag){
            temp.pre.next=temp.next;
            //下面这条语句成立有条件(判断删除的元素是不是最后一个元素)
            if (temp.next!=null){
                temp.next.pre=temp.pre;
            }
        }else {
            System.out.printf("没有%d号英雄节点,删除失败\n",no);
        }
    }
}
//创建类HeroNode(英雄类)当做链表的节点
class HeroNode2{
    public int no;    //英雄编号
    public String name;   //英雄名字
    public String nickName;   //英雄昵称
    public HeroNode2 next;    //指向下一个节点
    public HeroNode2 pre;    //指向上一个节点

    //创建构造函数,初始化值
    public HeroNode2(int no,String name,String nickName){
        this.no=no;
        this.name=name;
        this.nickName=nickName;
    }

    //为了方便输出,重写toString方法(因为此类中有next属性,为了防止重复打印后面的节点,所以省略next)


    @Override
    public String toString() {
        return "HeroNode2{" + "no=" + no + ", name='" + name + '\'' + ", nickName='" + nickName + '\'' + '}';
    }
}

3.4单向环形链表应用场景

1.Josephu(约瑟夫、约瑟夫环) 问题
2.Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
3.提示:用一个不带头结点的循环链表来处理 Josephu 问题:先构成一个有 n 个结点的单循环链表,然后由 k 结点起从 1 开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直到最后一个结点从链表中删除算法结束。
在这里插入图片描述

3.5单向环形链表介绍

在这里插入图片描述

3.6Josephu问题

@约瑟夫问题的示意图
在这里插入图片描述
@Josephu问题
Josephu 问题为:设编号为 1,2,… n 的 n 个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1 开始报数,数到
m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由此
产生一个出队编号的序列。
@提示
用一个不带头结点的循环链表来处理 Josephu 问题:先构成一个有 n 个结点的单循环链表,然后由 k 结点起从 1 开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直到最后一个结点从链表中删除算法结束。
@约瑟夫问题-创建环形链表的思路图解
在这里插入图片描述
@约瑟夫问题-小孩出圈思路分析
在这里插入图片描述

3.7Josephu代码实现

//约瑟夫问题
public class Josephu {
    public static void main(String[] args) {
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.add(5);
        //circleSingleLinkedList.list();
        circleSingleLinkedList.countBoy(1,2,5);
    }
}

//创建环形单向链表
class CircleSingleLinkedList{
    //定义一个第一个first小孩
    private Boy first=null;

    //添加小孩
    //参数sums:添加小孩的个数
    public void add(int sums){
        //校验sums是否正确
        if (sums<1){
            System.out.println("添加小孩个数有误,添加失败!");
            return;
        }
        Boy curBoy=null;   //辅助指针,用于构建环形链表
        //通过遍历添加小孩
        for (int i=1;i<=sums;i++){
            Boy boy = new Boy(i);
            //判断是否是第一个小孩
            if (first==null){
                 first=boy;      //将first指针指向第一个小孩(first指针不可移动)
                 curBoy=boy;     //用于构建环形链表
                 boy.setNext(first);   //将第一个小孩形成环状
            }else {
                 curBoy.setNext(boy);    //将添加的小孩节点与此前最后一个节点相连
                 boy.setNext(first);    //将添加的小孩节点与第一个节点相连
                 curBoy=boy;            //将辅助指针后移
            }
        }
    }
    //遍历
    public void list(){
        //校验链表
        if (first==null){
            System.out.println("链表为空");
            return;
        }
        //定义辅助指针遍历
        Boy curBoy=first;
        //遍历
        while (true){
            System.out.printf("出圈小孩的编号为:%d \n",curBoy.getNo());
            if (curBoy.getNext()==first){   //此刻已经遍历结束
                break;
            }
            curBoy=curBoy.getNext();   //将指针后移(因为是private修饰,所以用get方法获取next属性)
        }
    }

    /**
     * 约瑟夫(小孩出圈问题)
     * @param startNum  开始起数的数字
     * @param count     每次数的个数
     * @param total     起始一共有的小孩数
     */
    public void countBoy(int startNum,int count,int total){
        //对参数进行校验
        if (first==null || startNum<1 || startNum>total){
            System.out.println("参数输入有误");
            return;
        }
        //定义一个辅助变量让其指向first
        Boy temp=first;
        //遍历链表让辅助变量指向first的前一个节点
        while (true){
            if (temp.getNext()==first){//说明temp就是first的前一个节点
                break;
            }
            temp=temp.getNext();
        }
        //按照要求让first指向要求的startNum位置(因为默认是按照first为1的位置计算)
        for (int i=0;i<startNum-1;i++){   //因为默认从1开始,所以遍历startNum-1次就可
            first=first.getNext();
            temp=temp.getNext();
        }
        //遍历让其出圈
        while (true){
            //判断是否出圈结束
            if (temp==first){  //说明圈中只有一个点
                break;
            }
            //遍历到该出圈的节点
            for (int i=0;i<count-1;i++){
                first=first.getNext();
                temp=temp.getNext();
            }
            System.out.printf("出圈小孩编号为:%d \n",first.getNo());
            temp.setNext(first.getNext());  //将first前一个节点指向first后一个节点(让中间节点出圈)
            first=first.getNext();  //将first节点后移
        }
        System.out.printf("最后一个小孩编号为:%d \n",temp.getNo());
    }
}

//创建小孩节点类
class Boy{
    private int no;   //小孩编号(因为此处定义变量是私有,所以后面定义get,set方法方便访问)
    private Boy next;  //指向下一个小孩的指针

    //定义构造方法初始化小孩编号
    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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值