约瑟夫问题

约瑟夫问题

  1. 设编号1,2........n的那个人围坐一圈,约定编号k(1<=k<=n)的人从开始报数,数到m的那个出列,从它的下一位又开始从1开始报数,数到m的那个人又出列,以此类推,直到剩下一个人;

    解决方法: 使用一个不带头节点的循环链表来处理,先构成一个有n个结点的循环链表,然后从k结点起1开始计数,记到m时,对应结点从链表中删除,然后再从被删除的结点的下一个结点又开始从1计数,直到剩下一个结点为止;

  2. 代码实现

    1)创建单链表

    ​ 先创建第一个结点,让first指向第一个结点,并形成环:first.next = first

    ​ 后面每创建一个结点,就加入到已有的环中;

    2)出圈的方法解析:

    ​ ① 需要创建一个辅助指针helper,让它总是指向最后一个结点的位置;

    ​ ② 假设起始位置为startNum, 那么helper指针和first都要移动startNum-1次;

    ​ ③ 然后小孩开始报数,假如报数m下,那么helper和first都要移动m-1次,移动后first位置的孩子为要出圈的孩子,则:first = first.next (first后移); helper.next = first;

    ​ ④ 直到first == helper的时候剩下一个结点;

    public class JosepfuList {
        public static void main(String[] args) {
            forList forList = new forList();
            forList.setFroArr(5);
         //   forList.getList();
           forList.countBoy(1,2,5);
        }
    }
    class forList {
        private Node first = null;
    
        //创建环形链表
        public void setFroArr(int nums) {
            if (nums<1) {
                System.out.println("输入正确的数量");
                return;
            }
            Node curr = null;  //辅助结点
            for (int i =1; i<=nums; i++) {
                // 创建结点
                Node body = new Node(i);
                if (i==1) {
                   first = body; //将该节点设置为first结点
                   curr = first;
                  curr.next = first;
                } else {
                    curr.next = body;
                    body.next = first;
                    curr = body;
                }
            }
        }
    
        // 根据用户输入,计算出小孩出小孩顺序
    
        /**
         *
         * @param startNo 从第几个开始
         * @param countNum 数几个数
         * @param nums 孩子总数
         */
        public void countBoy(int startNo, int countNum, int nums){
             if (startNo < 1 || countNum > nums || first==null) {
                 System.out.println("请输入正确数据");
                 return;
             }
             Node helper = first; // 辅助指针,让其指向最后一个结点
            while (true) {
                if (helper.next == first) {
                    break;
                }
                helper =helper.next;
            }
            /**
             * 在报数前让first和helper移动到startNo-1次
             */
           for (int i=0;i<startNo-1;i++) {
               helper = helper.next;
               first = first.next;
           }
           // 小孩开始报数,让first和helper指针同时移动countNum-1次,然后出圈,直到圈中只有一个结点
    
            while (true) {
                if (first == helper) {
                   break;
                }
               //让first和helper指针同时移动countNum-1次,first指向要出圈的结点
               for (int j=0; j<countNum-1; j++) {
                   first = first.next;
                   helper = helper.next;
               }
                System.out.println(first.no);
               first = first.next;
               helper.next = first;
            }
            System.out.println(first.no);
        }
    
        // 遍历环形链表
        public void getList() {
            if (first == null) {
                System.out.println("链表为null");
                return;
            }
            Node curr = first;
            while (true) {
                if (curr.next == first) {
                    System.out.println(curr.no);
                   return;
                }
                System.out.println(curr.no);
                curr = curr.next;
            }
        }
    }
    // 创建一个结点
    class Node {
        public int no;
        public Node next;
    
        public Node(int no) {
            this.no = no;
        }
    
        @Override
        public String toString() {
            return "Node{" +
                    "no=" + no +
                    '}';
        }
    }
    

posted @ 2021-04-13 17:04  撑起一片阳光  阅读(58)  评论(0编辑  收藏  举报