數據結構與算法 單向環形鏈表解決約瑟夫環問題

在前面我們瞭解和實現了環形隊列和鏈表,這篇博客我們來介紹和實現一種環形鏈表來解決經典的約瑟夫環問題。

環形鏈表其實與單鏈表大同小異,只不過環形鏈表是首尾相連的。由於環形鏈表的結構特點比較符合約瑟夫環,所以我們使用它來解決約瑟夫環題。在此之前,我們來了解一下什麼是約瑟夫環問題(這裏描述的比較血腥,我們也可以理解爲類似小時候玩的小遊戲-丟手絹。):

約瑟夫問題是個有名的問題:N個人圍成一圈,從第一個開始報數,第M個將被殺掉,最後剩下一個,其餘人都將被殺掉。例如N=6,M=5,被殺掉的順序是:5,4,6,2,3,1。
分析:
(1)由於對於每個人只有死和活兩種狀態,因此可以節點數據域標記每個人的狀態,可用true表示死,false表示活。
(2)開始時每個人都是活的,所以節點初值全部賦爲false。
(3)模擬殺人過程,直到所有人都被殺死爲止。

在這裏插入圖片描述

  • 實現前我們來做一些規定:
  1. 初始化環時,可指定節點個數N和出環值M。
  2. 環形鏈表首尾相連,每個節點都有值。
  3. 我們用從小到大的整數作爲節點的值,大小從1開始。初始化時,最大的值節點指向最小值節點。
  4. 節點出環時,被刪除(該節點的前一節點指向後一節點)。
  • 代碼示例:
public class CircularLinkList {
    private Node startNode;
    //the number of nodes of list
    private int N;
    //thM node which should be removed.
    private int M;
    class Node{
        private int number;
        private Node nextNode;

        public int getNumber() {
            return number;
        }

        public void setNumber(int number) {
            this.number = number;
        }

        public Node getNextNode() {
            return nextNode;
        }

        public void setNextNode(Node nextNode) {
            this.nextNode = nextNode;
        }
    }

    public CircularLinkList(int N, int M){
        this.startNode = new Node();
        this.startNode.setNumber(1);
        this.N = N;
        this.M = M;
        //create a temp node as a mark node which should point the start node.
        Node temp = this.startNode;
        for(int i = 2;i <= this.N;i ++){
            //create a next node
            Node node = new Node();
            node.setNumber(i);
            //previous node point the new node
            temp.setNextNode(node);
            //update previous node
            temp = node;
        }
        //make the mark node point start node.
        temp.setNextNode(this.startNode);
    }

    /**
     * print out all nodes' value
     */
    public void removeAndPrintNodeAsProvidedM(){
        //作爲測試使用
        int count = 0;
        
        //previous node of target node
        Node temp = this.startNode;
        if(this.N == 1){
            System.out.println(this.startNode.getNumber());
            -- this.N;
            return;
        }

        //因爲我們要開始就把temp賦值爲this.startNode,
        // 但是temp的含有是previous node of target node
        //所以第一次循環要少前進一次
        for(int i = 1;i < this.M - 1;i ++){
            temp = temp.getNextNode();
        }
        System.out.println(temp.getNextNode().getNumber());
        temp.setNextNode(temp.getNextNode().getNextNode());
        -- this.N;
        ++ count;

        while (this.N != 0){
            for(int i = 1;i < this.M;i ++){
                temp = temp.getNextNode();
            }
            System.out.println(temp.getNextNode().getNumber());
            temp.setNextNode(temp.getNextNode().getNextNode());
            -- this.N;
            ++ count;
        }
        System.out.println(count);
    }

    public static void main(String[] args) {
        CircularLinkList circularLinkList = new CircularLinkList(100000, 100);
        circularLinkList.removeAndPrintNodeAsProvidedM();
    }
}
  • 這幾天一直沒時間實現這段代碼。代碼實現感覺比較冗餘,應該可以優化。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章