约瑟夫环问题

这里写图片描述

思路1:链表模拟

普通解法,用链表的方法去模拟这个过程,N个人看作是N个链表节点,节点1指向节点2,节点2指向节点3,……,节点N-1指向节点N,节点N指向节点1,这样就形成了一个环。然后从节点1开始1、2、3……往下报数,每报到M,就把那个节点从环上删除。下一个节点接着从1开始报数。最终链表仅剩一个节点。它就是最终的胜利者。
这里写图片描述
缺点:
要模拟整个游戏过程,时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。

思路2:递推式

考虑下面的情景:

假如进行第10次游戏的时候某玩家胜利了,他记住了自己在开始时的位置为X,那下一轮开始时他会选择站在哪?

因为他知道在第11次游戏第一轮后肯定有一个人会淘汰,这时候就变成了10人的游戏,所以只要在一轮过后他站在了上一轮自己开始时候的位置就稳赢了!

由于游戏每一轮结束后每个人的位置相当于向左移动了M步,所以第11次开始他就直接站在上一轮开始位置靠右M个的位置,这样第11次游戏第一轮结束后每个玩家向左移动M位正好站在了他第10次游戏胜利的位置,这时候游戏人数也变成了10人,相当于完全变成了上一轮的游戏,所以这一次游戏他也肯定赢定了!

根据上面场景可以推出递推式:
这里写图片描述
这里写图片描述
这里写图片描述
因为求出的结果是数组中的下标,最终的编号还要加1

直接根据递推式写出的代码:

    /*
     * 直接用递推式求出胜利者编号!
     */
    public int yuesefu2(int totalNum,int countNum) {
        int p = 0;
        for(int i =2;i<=totalNum;i++) {
            p = (p+countNum)%i;
        }
        return p+1;
    }
    @Test
    public void test1() {
        System.out.println(yuesefu2(5, 2));//直接返回最终胜利玩家的编号3
    }
}

利用公式求出每一轮移除的元素:

/**
 * 最后剩下的数字
 */ 
import java.util.ArrayList;
import java.util.List;

public class LastNumberInCircle {

    public void lastRemaining(int totalNum,int index){
        //初始化人数
        List<Integer> start = new ArrayList<Integer>();
        for(int i = 0;i< totalNum;i++){
            start.add(i);
        }
        //从第k个开始计数
        int k = 0;
        while(start.size() > 0){
            k = k+ index;
            //第m个人的索引位置
            k = k %(start.size()) -1;
            //判断是否到队尾
            if(k < 0){
                System.out.println(start.get(start.size()-1));
                start.remove(start.size()-1);
                k = 0;
            }else{
                System.out.println(start.get(k));
                start.remove(k);
            }
        }
    }
}

运行结果如下:
这里写图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值