约瑟夫问题

题目描述

据说著名犹太历史学家 Josephus 有过以下故事:在罗马人占领乔塔帕特后,39 个犹太人与 Josephus 及他的朋友躲到一个洞中,39 个犹太人决定宁愿死也不要被敌人抓到,于是决定了一种自杀方式,41 个人排成一个圆圈,由第 1 个人开始报数,报数到 3 的人就自杀,然后再由下一个人重新报 1,报数到 3 的人再自杀,这样依次下去,直到剩下最后一个人时,那个人可以自由选择自己的命运。这就是著名的约瑟夫问题。现在请用单向环形链表得出最终存活的人的编号。

题目分析:

本例可以使用链表(包括单向循环链表或者双向循环链表),数组来完成,具体可以根据自己需求或者熟悉哪一种。个人感觉循环链表更加形象,易于理解和操作。

代码实现:

package 链表;

public class Main {
    public static void main(String[] args){
        JosePhus josePhus = new JosePhus();
        josePhus.getResult(39,3);
    }
}

class InfoNode{
    public int id;
    public InfoNode next;

    public InfoNode(int id){
        this.id = id;
    }

    @Override
    public String toString() {
        return "InfoNode[" +
                "id=" + id +
                ']';
    }
}

class JosePhus{

    public InfoNode start = null;
    private void addBoyNum(int n) {
        System.out.println("链表数据校验......");
        if (n < 1 ){
            System.out.println("n必须大于等于1");
            return;
        }
        System.out.println("正在创建链表......");

        InfoNode temp = null; //用于控制后续的添加操作
        for (int i = 1; i <= n ; i++) {
            InfoNode infoNode = new InfoNode(i);
            if (i == 1){
                start = infoNode; //给第一个节点赋值
                start.next = start;//指向自己
                temp = start; //给temp赋值以后 后续的增加节点操作就用它代替

            }else {
                temp.next = infoNode;
                temp = temp.next;//然后temp后移
                temp.next = start;//后移完成以后 再指向头部
            }
        }
        System.out.println("环形链表创建完成~~~~");

    }

   public void getResult(int n, int m){
       //1.先调用方法创建一个有n节点的环形链表
       addBoyNum(n);

       //2.校验数据是否正确
       if (m > n  || m > 1000 || m < 1){
           System.out.println("输入的n或m不符合要求~~~~");
           return;
       }

       InfoNode temp = start;
       //3.出圈的时候要让temp始终位于start之前,所以接下来要让他先跑到最后一个位置
       while (true){
           if (temp.next == start){
               break;
           }
           temp = temp.next;
       }
       //4.temp走到指定位置 开始出圈
       while (true){
           if (temp == start){
               System.out.println("遍历结束!");
               break;
           }

           for (int i = 1; i <= m-1  ; i++) {
                start = start.next;
                temp = temp.next;
           }
           System.out.println("出圈节点编号:"+start.id);
           //去除出圈的节点
           start = start.next;
           temp.next = start;
       }
       //当退出整个循环以后 说明只剩一个节点,就是幸存的人
       System.out.println("幸存者id是:"+start.id);
   }
}

最后:

LeetCode自带的编译器结果不让过,但是idea里是没问题的,很懵......不知道这个是什么情况,还没玩懂怎么操作~~~~~蓝瘦
原文地址:https://www.cnblogs.com/coding-996/p/12263407.html