数据结构与算法 单向环形链表解决约瑟夫环问题

在前面我们了解和实现了环形队列和链表,这篇博客我们来介绍和实现一种环形链表来解决经典的约瑟夫环问题。

环形链表其实与单链表大同小异,只不过环形链表是首尾相连的。由于环形链表的结构特点比较符合约瑟夫环,所以我们使用它来解决约瑟夫环题。在此之前,我们来了解一下什么是约瑟夫环问题(这里描述的比较血腥,我们也可以理解为类似小时候玩的小游戏-丢手绢。):

约瑟夫问题是个有名的问题: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();
    }
}
  • 这几天一直没时间实现这段代码。代码实现感觉比较冗余,应该可以优化。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章