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}出圈
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章