Java | Josephu(约瑟夫)问题

Josephu问题

/*
 * Josephu问题:
 *  设编号为1,2,...,n的n个人围坐一圈,约定编号为k(1 <= k <= n)的人从1开始报数,数到m的那个人出列,它的下一位
 *  又从1开始报数,数到m的那个人又出列,依此类推,直到所有人出列为止,由此产生一个出队编号的序列。
 *
 * 用一个不带头结点的循环链表来处理Josephu问题:
 *  构建一个单项的环形链表思路:
 *      1. 先创建第一个节点,让first指向该节点,并形成环形
 *      2. 后面当我们每创建一个新的节点,就把该节点加入到已有的环形链表中即可
 *  遍历环形链表
 *      1. 先让一个辅助指针(变量)curBoy,指向first节点
 *      2. 然后通过一个while循环遍历该环形链表即可
 * 举例:
 *  n = 5,即有5个人
 *  k = 1,从第一个人开始报数
 *  m = 2,数2下
 *  出圈的顺序:2 -> 4 -> 1 -> 5 -> 3
 */

Boy类

class Boy{
    private int id;
    private Boy next;

    public Boy(int id) {
        this.id = id;
    }

    public Boy(Boy boy) {
        this.id = boy.id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Boy getNext() {
        return next;
    }

    public void setNext(Boy next) {
        this.next = next;
    }

    @Override
    public String toString() {
        return "Boy{" +
                "id=" + id +
                '}';
    }
}

CircleSingleLinkedList类

class CircleSingleLinkedList{
    private Boy first = null;
    // 添加小孩节点,构建成一个环形链表
    public void addBoys(int nums){
        if (nums < 1)
            throw new RuntimeException("小孩数量有误!");
        Boy boy = null;
        Boy curBoy = null;
        for (int i = 1; i <= nums; i++) {
            boy = new Boy(i);
            if (first == null){
                first = new Boy(boy);
                first.setNext(first);
                continue;
            }
            curBoy = first;
            while (curBoy.getNext().getId() != first.getId()){
                curBoy = curBoy.getNext();
            }
            curBoy.setNext(boy);
            boy.setNext(first);
        }
    }
    // 遍历当前的环形链表
    public void showBoy(){
        Boy curBoy = first;
        System.out.println(first);
        while (curBoy.getNext() != null){
            curBoy = curBoy.getNext();
            if (curBoy.getId() == first.getId())
                return;
            System.out.println(curBoy);
        }
    }
    // 根据用户的输入,计算出小孩出圈的顺序
    public void countBoy(int startId,int countNum,int nums){
        addBoys(nums);
//        showBoy();
        Boy curBoy = first;
        int count = 1;  // flag = 0时用于记录是否开始游戏,flag = 1时用于记录是否到达countNum
        boolean startGame = false;   // 判断是否开始游戏,即第一次循环到编号startId的小孩
        while (curBoy.getNext() != null){
            curBoy = curBoy.getNext();
            if (curBoy.getNext().getId() == first.getId())
                break;
        }
        while (curBoy.getNext() != curBoy){
            if (!startGame && count == startId){
                count = 1;
                startGame = true;
            }
            if (startGame && count == countNum){
                System.out.println(curBoy.getNext() + "出圈");
                curBoy.setNext(curBoy.getNext().getNext());
                count = 1;
            }
            curBoy = curBoy.getNext();
            count ++;
        }
        System.out.println(curBoy + "出圈");
    }
}

代码测试

@Test
public void test(){
    CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
    circleSingleLinkedList.countBoy(1,2,5);
}

结果输出

Boy{id=2}出圈
Boy{id=4}出圈
Boy{id=1}出圈
Boy{id=5}出圈
Boy{id=3}出圈
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值