X2-2、java數(shù)據(jù)結構---約瑟夫問題解決【2020-11-28】

總目錄:地址如下看總綱

http://www.itdecent.cn/p/929ca9e209e8

1、什么是約瑟夫問題

約瑟夫問題其實也是一個游戲(丟手絹),也稱為約瑟夫環(huán)。
規(guī)則很簡單:就是有一群小鬼圍起來(例5個)成一個圈,先從第一個小鬼報數(shù) n,以第一個小鬼的第n 個小鬼,
既出列,出列的 第 n 個小鬼的后面一個 繼續(xù)報數(shù) n ,依次類推直到所有小鬼都離開這個圈中


image.png

解決方案:可以使用單向環(huán)形鏈表

2、單向環(huán)形鏈表的構建和遍歷分析

image.png

1、構建一個單向的環(huán)形鏈表思路
(1). 先創(chuàng)建第一個節(jié)點, 讓 first 指向該節(jié)點,并形成環(huán)形
(2). 后面當我們每創(chuàng)建一個新的節(jié)點,就把該節(jié)點,加入到已有的環(huán)形鏈表中即可
2、遍歷環(huán)形鏈表
(1). 先讓一個輔助指針(變量) curKid,指向first節(jié)點
(2). 然后通過一個while循環(huán)遍歷 該環(huán)形鏈表即可 curKid.next == first 結束

3、單向環(huán)形鏈表抓鬼

image.png

根據(jù)用戶的輸入,生成一個小鬼出圈的順序(方法:countKid)
n = 5 , 即有5只鬼
k = 1, 從第一個小鬼開始報數(shù)
m = 2, 數(shù)2下(包括它自己也得數(shù))

  1. 需求創(chuàng)建一個輔助指針(變量) helper (討債鬼), 事先應該指向環(huán)形鏈表的最后這個節(jié)點.
    2.小鬼報數(shù)前,先讓 first 和 helper 移動 k - 1次
  2. 當小鬼報數(shù)時,讓first 和 helper 指針同時 的移動 m - 1 次
  3. 這時就可以將first 指向的小鬼節(jié)點 出圈
    first = first .next
    helper.next = first
    原來first 指向的節(jié)點就沒有任何引用,就會被回收
    helper (討債鬼):作用就是一直跟在first后面,得到first不行了就把鎖鏈套在下一個first脖子,直達所有都套死

出圈的順序
2->4->1->5->3

3、代碼實現(xiàn)

package com.kk.datastructure.linkedlist.josepfu;

/**
 * title: 約瑟夫問題--單向環(huán)形鏈表
 * @author 阿K
 * 2020年11月28日 下午7:56:21 
 */
public class Josepfu {

    public static void main(String[] args) {
        // 測試一把構建環(huán)形鏈表,和遍歷
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addKid(5);// 加入5個小鬼節(jié)點
        circleSingleLinkedList.showKid();

        // 測試一把小鬼出圈
        circleSingleLinkedList.countKid(1, 2, 5); // 2->4->1->5->3
    }
}

//創(chuàng)建一個環(huán)形的單向鏈表
class CircleSingleLinkedList {
    // 創(chuàng)建一個first節(jié)點,當前沒有編號
    private Kid first = null;

    // 添加小鬼節(jié)點,構成一個環(huán)形鏈表(并非一個個添加,而是初始化個數(shù))
    public void addKid(int nums) {
        if (nums < 1) {
            System.out.println("數(shù)量錯誤,無法構建環(huán)形");
            return;
        }
        // 輔助指針,幫助構建環(huán)形鏈表
        Kid curKid = null;

        // 使用for來創(chuàng)建我們的環(huán)形鏈表
        for (int i = 1; i <= nums; i++) {
            // 根據(jù)編號,創(chuàng)建小鬼節(jié)點
            Kid kid = new Kid(i);
            // 如果是第一個小鬼
            if (i == 1) {
                first = kid;
                first.setNext(first);// 構成環(huán)
                curKid = first; // 讓curKid指向第一個小鬼
            } else {
                curKid.setNext(kid);// 此時的輔助是指向 first,它后面則指向 新節(jié)點(編號 2...)
                kid.setNext(first); // 構成環(huán)
                curKid = kid; // 此時curKid 指向第二個小鬼....
            }
        }
    }

    // 遍歷當前環(huán)形鏈表
    public void showKid() {
        // 判空
        if (first == null) {
            System.out.println("沒有任何小鬼~~");
            return;
        }

        // 因為first不能動,因此我們?nèi)匀皇褂靡粋€輔助指針完成遍歷
        Kid curKid = first;
        while (true) {
            System.out.printf("小鬼的編號 %d \n", curKid.getNo());
            if (curKid.getNext() == first) {
                // 已經(jīng)遍歷結束
                break;
            }
            // 后移
            curKid = curKid.getNext();
        }
    }

    /**
     * 開始抓鬼
     * 
     * @param startNo  表示從第幾個小鬼開始數(shù)數(shù)
     * @param countNum 表示每次數(shù)了幾只小鬼(報數(shù)幾)
     * @param nums     表示最初圈內(nèi)的總鬼數(shù)
     */
    public void countKid(int startNo, int countNum, int nums) {
        // 數(shù)據(jù)校驗
        if (first == null || startNo < 1 || startNo > nums) {
            System.out.println("參數(shù)輸入有誤, 請重新輸入");
            return;
        }

        // 創(chuàng)建輔助指針,討債鬼,作用催命
        Kid helper = first;// 初始化自己套自己,因為圈內(nèi)目前就一個
        // 需求創(chuàng)建一個輔助指針(變量) helper , 事先應該指向環(huán)形鏈表的最后這個節(jié)點
        while (true) {
            if (helper.getNext() == first) {
                // 已經(jīng)到最后一個
                break;
            }
            // 后移
            helper = helper.getNext();
        }

        // 考慮到 可能不是 k=1的情況:
        // 小鬼報數(shù)前,先讓 first 和 helper 移動 k - 1(startNo - 1)次
        for (int j = 0; j < startNo - 1; j++) {
            first = first.getNext();
            helper = helper.getNext();
        }

        // 當小鬼報數(shù)時,讓first 和 helper 指針同時 的移動 m - 1 次(countNum -1), 然后出圈
        // 這里是一個循環(huán)操作,直到圈中只有一個節(jié)點
        while (true) {
            if (helper == first) {
                // 說明圈中只有一個節(jié)點
                break;
            }
            // 讓 first 和 helper 指針同時 的移動 countNum - 1
            for (int j = 0; j < countNum - 1; j++) {
                first = first.getNext();
                helper = helper.getNext();
            }
            // 這時first指向的節(jié)點,就是要出圈的小鬼節(jié)點
            System.out.printf("小鬼%d出圈\n", first.getNo());
            // 這時將first指向的小鬼節(jié)點出圈
            first = first.getNext();
            helper.setNext(first); // 討債鬼的繩索套在了新frist脖子上

        }
        System.out.printf("最后留在圈中的小鬼編號%d \n", first.getNo());
    }
}
//創(chuàng)建一個Kid類,表示一個節(jié)點
class Kid{
    private int no;// 編號
    private Kid next;// 指向下一個節(jié)點
    
    public Kid(int no) {
        this.no = no;
    }
    public int getNo() {
        return no;
    }
    public void setNo(int no) {
        this.no = no;
    }
    public Kid getNext() {
        return next;
    }
    public void setNext(Kid next) {
        this.next = next;
    }
}

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容