单向环形链表解决约瑟夫(Josephu)问题

98 阅读1分钟

代码

package top.snailstudy.linkedlist;

public class Josephu {
    public static void main(String[] args) {
        CircleSingleLinkedList list = new CircleSingleLinkedList();
        list.addBoy(5);
        list.showBoy();
        list.countBoy(1,2,5);
    }
}

class CircleSingleLinkedList{
    //创建一个first节点,当前没有编号
    private Boy first = null;
    //添加节点,构建成环形链表
    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;
                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;
        }
        Boy curBoy = first;
        while (true){
            System.out.printf("节点的编号%d\n",curBoy.getNo());
            if(curBoy.getNext() == first){//遍历完毕
                break;
            }
            curBoy = curBoy.getNext();//后移
        }
    }

    //根据用户的输入,计算出出圈的顺序
    /*
    * 分析:生成一个节点出圈顺序,n=5,即有5人;k=1,从第一个人开始报数;m=2,数两下
    * 创建一个辅助指针(变量)helper,事先应该指向环形链表的最后这个节点
    * 当节点报数时,让first和helper指针同时的移动 m-1 次
    * 这时就可以将first指向的节点出圈
    * first = first.next
    * helper.next = first
    * 原来first指向的节点就没有任何引用,就会被回收
    * startNo :表示从第几个节点开始报数
    * countNum : 表示数几下
    * nums : 表示最初有多少个节点在圈中
    * */
    public void countBoy(int startNo,int countNum,int nums){
        if(first == null || startNo < 1 || startNo > nums){
            System.out.println("参数输入有误");
            return;
        }
        Boy helper = first;
        while (true){
            if(helper.getNext() == first){//说明helper指向了最后节点
                break;
            }
            helper = helper.getNext();
        }
        //报数前,先让first和helper移动 k - 1
        for (int i = 0; i < startNo - 1; i++) {
            first = first.getNext();
            helper = helper.getNext();
        }
        //当节点报数时,让first和helper指针同时的移动 m-1 次
        while (true){
            if(helper == first){//圈中只有一个节点
                break;
            }
            for (int i = 0; i < countNum - 1; i++) {
                first = first.getNext();
                helper = helper.getNext();
            }
            System.out.printf("节点%d出圈\n",first.getNo());
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.printf("最后留在圈中的节点%d \n",first.getNo());
    }
}


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;
    }
}

输出结果 节点的编号1 节点的编号2 节点的编号3 节点的编号4 节点的编号5 节点2出圈 节点4出圈 节点1出圈 节点5出圈 最后留在圈中的节点3