用LinkedList解决,约瑟夫问题(简单解法)

约瑟夫问题介绍

约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3。(出列也一样~)

1.暴力遍历方法

问题改造:假设有n个人围成一圈,从第m个开始报数,报到k出列;给出出列顺序;
直接暴力模拟一个人报一个数,方便理解,实现如下:

1.1思路说明:

 /**
     * 整体思路:
     * 1.使用LinkedList可以方便的移除元素,模拟出列;remove方法;
     * 2.定义一个变量i(指针)指向当前报数的元素;再定义一个计数器count变量,用来计数;
     * 3.两个不断一点点后移(++),直到只剩一个人时 即 .size()==1,直接出列即可;
     * 在一次次位移过程中:
     *    3.1如果i位移后超出当前链表长度时;说明报数完一圈了,i应该指向第一个人,即i=0;
     *    3.2如果count达到key,应该出列;remove()
     *       3.2.1 出列后应该将 计数器count复位,count=1;
     *       3.2.2 出列后,还要判断当前指针后没有越界;(如果最后一个元素出列,那么此时指针指向就界外了,同3.1)
     * @param allNum  总人数
     * @param begin  开始的人编号
     * @param key 出列的人的编号
     */

1.2代码实现:

private static void josephus(int allNum, int begin, int key) {
        //创建一个长度为allNum的集合;
        LinkedList<Integer> studentCircle=new LinkedList();
        //初始化数据:给每个位置的”人“,发一个编号;从1开始到allNum;
        for (int i = 0; i < allNum; i++) {
            studentCircle.add(i+1);
        }


        int i=begin-1;//开始编号对应的LinkedList下标(下标越界时,说明报完一圈了,将i置0从头接着报数(报的数不变))
        int count=1;//用count记录报数(有人出列时,恢复初始值)

        //剩最后一个人时游戏结束,直接出列;否则不断报数;
        while (studentCircle.size()!=1){
            if (i>studentCircle.size()-1){//判断当前有没有越界(有没有报完一圈)
                i=0;
            }
            if (count==key){//判断此人该不该出列
                System.out.println(studentCircle.get(i)+"号出列");//出列;
                studentCircle.remove(i);//移除出列的人(元素);
                if (i>studentCircle.size()-1){//移除完人以后,判断当前下标越没越界(特殊情况移除末尾,这个i已经需要从0开始)
                    i=0;
                }
                count=1;//计数复位,重新数;
            }
            i++;//下标后移
            count++;//下一个人报的数
        }
        //出了while循环,队伍只剩一个人了;直接出列;
        System.out.println(studentCircle.get(0)+"出列");

    }

1.3测试:

 	/**假设有13个人,从第三个人开始报数,数到5出列;
     *结果:7 12 4 10 3 11 6 2 1 5 9 13 8
     */
    public static void main(String[] args) {
        josephus(13,3,5);
    }

结果:
在这里插入图片描述

2.直接定位法

2.1思路说明:

每轮直接计算出一个出圈的人(下标);注意是否越界;
直接出圈,然后剩下的人接着计算;注意是否越界;
每轮出一个,出n-1轮后剩最后一个,直接出;

2.2代码实现:

    private static void josephus(int allNum, int begin, int key) {
        //创建一个长度为allNum的集合;
        LinkedList<Integer> studentCircle=new LinkedList();
        //初始化数据:给每个位置的”人“,发一个编号;从1开始到allNum;
        for (int i = 0; i < allNum; i++) {
            studentCircle.add(i+1);
        }
        
        int i=begin-1;//开始编号对应的LinkedList下标
        
        for (int j = 0; j < allNum-1; j++) {//n-1轮(不能用链表长度,会随移除变短)
                i=i+key-1;//直接找到下标(判断是否超过一圈了)//举个例子试一下即可知道关系
                if (i<studentCircle.size()){//没越界,直接移除;
                    System.out.println(studentCircle.get(i)+"号出列");
                    studentCircle.remove(i);
                    if (i>studentCircle.size()-1){//移除完人以后,判断当前下标越没越界(特殊情况移除末尾,这个i已经需要从0开始)
                        i=0;
                    }
                }else {//越界了,直接定位到需移除位置
                    i=i%studentCircle.size();//举个例子试一下即可知道关系
                    System.out.println(studentCircle.get(i)+"号出列");
                    studentCircle.remove(i);
                    if (i>studentCircle.size()-1){//移除完人以后,判断当前下标越没越界(特殊情况移除末尾,这个i已经需要从0开始)
                        i=0;
                    }
                }
        }
        //出了循环,队伍只剩一个人了;直接出列;
        System.out.println(studentCircle.get(0)+"号最后出列");
    }

2.3测试结果同1.3

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值