约瑟夫环问题

约瑟夫环(单向环形链表)

准备结点类

class Boy {
    private int no;
    private Boy next; //指向下一个结点
}

准备链表类

class CircleSingleLinkedList {
    private Boy first = null;
}

构建一个约瑟夫环

思路:

1. 创建第一个结点,让first,curBoy指向该结点,并形成环形
2. 后面每创建一个新的结点,就把该结点,加入到已有的环形链表中

注意:

​ 第一个结点创建完后curBoy指向第一个结点并且不发生移动,并且该结点指向自己,形成环状

​ 后面创建的每一个结点都先让curBoy的next指向该结点,然后该结点next指向first

​ 然后向后移动curBoy指针,curBoy可以理解为一个指向链尾的指针

public void addBoy(int nums) {
        if (nums < 1) {
            System.out.println("nums值不正确");
            return;
        }
        Boy curBoy = null;
        for (int i = 1; i <= nums; i++) {
            Boy boy = new Boy(i);
            if (i == 1) {
                first = boy;
                boy.setNext(first);
                curBoy = first;
            } else {
                curBoy.setNext(boy);
                boy.setNext(first);
                curBoy = boy;
            }
        }
    }

打印约瑟夫环

思路:

​ 指针首先指向first,然后循环向后移动,当指针的下一个结点为first时,说明环已经遍历完成,结束循环

public void showBoys() {
        //判断链表是否为空
        if (first == null) {
            System.out.println("没有任何小孩!");
            return;
        }
        Boy curBoy = first;
        while (true) {
            System.out.printf("Boy %d\n", curBoy.getNo());
            if (curBoy.getNext() == first) break;
            curBoy = curBoy.getNext();
        }
    }

小孩出圈

问题描述:

有一圈小孩围坐在一起,每个小孩都按顺序标号,从指定标号小孩开始报数,报到n,这个小孩就出圈,直至圈中只剩一个小孩,求出圈序列

思路:

因为是单向链表,所以删除待删结点还是需要获取到前一个结点

默认待删结点是第一个结点,first指向它,然后创建一个helper用于指向前一个结点

定位开始报数的小孩标号startNo, first 和 helper 需要移动 startNo - 1 次

开始报数:报数到n,first 和 helper 需要移动 n -1 次

找到出圈结点,先打印结点序号,然后 first 后移一位,即指向下一次第一个报数的小孩

然后helper的next指向first,因为这是个重复的过程,所以当圈中只剩一个结点时,循环结束 ,即 first = helper

/**
     * @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;
        }
        //创建一个辅助结点,指向待删结点的前一个结点
        //刚开始指向第一个结点first的前一个结点
        Boy helper = first;
        while (helper.getNext() != first) helper = helper.getNext();
        //小孩报数前,先让 first 和 helper 移动 k-1 次,first指向第一个报数的小孩
        //假如k=1,则不需要移动,first默认指向第一个,k=2,则需要移动一次
        for (int i = 0; i < startNo-1; i++) {
            first = first.getNext();
            helper = helper.getNext();
        }
        //小孩报数时,报数到countNum,first 和 helper 需要移动 countNum-1 次
        //假如countNum报数到1,则first不需要移动,countNum=2,则需要移动 1 次
        //这里是一个循环的操作,直到圈中只有一个结点,每次小孩出圈,都要重新从1开始报数
        while(first!=helper){
            for (int i = 0; i < countNum-1; i++) {
                first = first.getNext();
                helper = helper.getNext();
            }
            //此时first指向出圈小朋友,helper指向前一个小朋友
            System.out.printf("%d ",first.getNo());
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.printf("%d\n",first.getNo());
    }
posted @ 2021-03-07 13:10  编程の小白  阅读(107)  评论(0编辑  收藏  举报