约瑟夫环——公式法——附LeetCode—剑指offer题目—剑指 Offer 62. 圆圈中最后剩下的数字

简介: 约瑟夫环——公式法——附LeetCode—剑指offer题目—剑指 Offer 62. 圆圈中最后剩下的数字

约瑟夫环问题:

      约瑟夫问题是个著名的数学问题,N个人围成一圈,从第一个人开始数,杀掉第M个人。然后从第M+1个人再开始数,数到第M个人再杀掉,然后再从第M+1个人开始数,数到第M个人再次杀掉,反复进行,直到这圈人只剩下最后一个人。游戏结束。


那么问题来了,如何快速的判断最后留下的那个人是谁呢?这就是约瑟夫环要研究的问题。


我们以数组为例研究,假设 N=8 , M=3,     F(8 , 3) = 7     (这个7是 7这个人的序号)


                               1      ,       2 ,       3 ,       4 ,       5 ,       6 ,       7,       8


第一个回合:杀掉第三个人数组变成    F(7 , 3) = 4  (这个1是 7这个人的序号)


                                       4 ,       5 ,       6 ,       7,       8,       1,       2


第二个回合:杀掉第三个人数组变成     F(6 , 3) = 1  (这个5是 7这个人的序号)


                                             7,       8,       1,       2,       4,       5


第三个回合:杀掉第三个人数组变成       F(5 , 3) = 4 在  (这个2是 7这个人的序号)


                                                 2,       4,       5,       7,       8


第四个回合:杀掉第三个人数组变成        F(4 , 3) = 1   (这个4是 7这个人的序号)


                                                     7,       8,       2,       4


第五个回合:杀掉第三个人数组变成        F(3 , 3) = 2   (这个1是 7这个人的序号)


                                                       4,       7,       8


第六个回合:杀掉第三个人数组变成(因为是个环,所以数完7是第二个时候,就重新回到4数作为第三个,因此下一个是删掉的4)   F(2 , 3) = 2 (这个1是 7这个人的序号)


                                                            4,       7


第七个回合:杀掉第三个人数组变成    F(1 , 3) = 1 (这个1是 7这个人的序号)


                                                                 7


我们可以看到最后剩下的是7号,暴力的办法是模拟整个过程,然后得到最终留下的人,但是在程序里这样做会导致时间复杂度非常高,具体的可以看我的下面python的暴力程序。


约瑟夫环的方法类似于找个规律,让我们可以快速的确定那个人可以留到最后,我们可以看到


F(8 , 3) = 7   , F(7 , 3) = 4 , F(8 , 3) =  F(7 , 3) + 3,因为每次杀掉第三个人之后,前两位也位移到数组最后面了,所以7号人的位置会左移三位,由7移动到4,相当于所在的下标 -3  。


这就是我们找到的规律,如果我们使用(倒推思想)让最后的一个函数 F(1 , 3)不停的+3 ,是不是就可以很快找到活到最后的人。


那么我们来这样做,由上面可知 F(1 , 3)  = 1, 那么按照我们找到的规律可得


                                                    F(2 , 3) = F(1 , 3)  + 3 = 1 + 3 = 4


但是我们发现F(2 , 3)的列表长度一共才2,序号4从何谈起呢?因此我们需要对我们每次的+3操作再加上一个操作,即对当前列表的长度进行取余。等式变成如下


                                    F(2 , 3) = (F(1 , 3)  + 3)%2 = (1 + 3 )% 2 =  4%2 = 2


因为每次移动到最后之后如果数组没有了,但是还没移动完,就回到第一个重新移动,这就是取余的原因,因为我们这个是个围成圈的嘛!


                                   F(3 , 3) = (F(2 , 3)  + 3)% 3 = (2 + 3 )% 3 =  5%3 = 2


                                   F(4 , 3) = (F(3 , 3)  + 3)% 4 = (2 + 3 )% 4 =  5%4 = 1


                                   F(5 , 3) = (F(4 , 3)  + 3)% 5 = (1 + 3 )% 5 =  5%5= 4


                                   F(6 , 3) = (F(5 , 3)  + 3)% 6 = (4 + 3 )% 6 =  7%6 = 1


                                   F(7 , 3) = (F(6 , 3)  + 3)% 7 = (1 + 3 )% 7 =  4%7 = 4


                                   F(8 , 3) = (F(7 , 3)  + 3)% 8 = (4 + 3 )% 8 =  7%8 = 7


我想到这里,这个规律我们已经看的很清楚了,即


                                      F(N , M) = (F(N-1 , M)  + M)%N


没错,这个公式就是我们的约瑟夫环的递推公司,用这个方法求解下面的剑指offer的习题即可


剑指 Offer 62. 圆圈中最后剩下的数字


难度简单610收藏分享切换为英文接收动态反馈


0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。


例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。


示例 1:


输入: n = 5, m = 3

输出: 3

示例 2:


输入: n = 10, m = 17

输出: 2

限制:


1 <= n <= 10^5

1 <= m <= 10^6

通过次数148,821提交次数226,172


第一种暴力法:

构造队列,deque,rotate函数可以旋转队列,使它真的变成圈

每次计算余数,然后进行队列将m之前的数放到队列最后,然后弹出最左边的数


image.png


时间有点长


Python代码:

class Solution:
    def lastRemaining(self, n: int, m: int) -> int:
        ans = collections.deque(list(range(n)))
        while len(ans)!=1:
                ans.rotate(-(m%len(ans))+1)#队列左移
                ans.popleft()  #移动完之后,弹出
        return ans[0]

第二种约瑟夫环法-递归思想代码:

Python代码:

class Solution:
    def lastRemaining(self, n: int, m: int) -> int:
        f = 0
        for i in range(2 , n+1): f = (f+m)%i
        return f

 C++代码:

class Solution {
public:
    int lastRemaining(int n, int m) {
        int f=0;
        for(int i=2; i<n+1; i++) f = (f+m)%i;
        return f;
    }
};
相关文章
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
57 6
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 26. 树的子结构
这篇文章提供了解决LeetCode上"剑指Offer 26. 树的子结构"问题的Python代码实现和解析,判断一棵树B是否是另一棵树A的子结构。
50 4
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 04. 二维数组中的查找
剑指Offer题目 "二维数组中的查找" 的Python解决方案,包括非递归迭代、递归以及使用内置函数的二分查找方法,以判断一个有序的二维数组中是否含有给定整数。
36 1
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 03. 数组中重复的数字
解决剑指Offer题目 "数组中重复的数字" 的Python实现方法,通过使用字典来记录数组中每个数字的出现次数,快速找出重复的数字。
39 1
|
1月前
|
程序员 C语言
【C语言】LeetCode(力扣)上经典题目
【C语言】LeetCode(力扣)上经典题目
|
2月前
|
SQL Oracle 关系型数据库
CASE WHEN 语句的语法及示例,LeetCode 题目 “确认率” 练习
本文介绍了SQL中CASE语句的两种形式和语法,并通过LeetCode题目“确认率”的SQL查询示例展示了CASE语句在实际问题中的应用,解释了如何使用CASE语句计算特定条件的比率。
|
3月前
|
算法
LeetCode第12题目整数转罗马数字
该文章介绍了 LeetCode 第 12 题整数转罗马数字的解法,通过使用 TreeMap 按照整数从大到小排序,先使用大的罗马数字表示整数,再用小的,核心是先表示完大的罗马数字,想通此点该题较简单。
LeetCode第12题目整数转罗马数字
|
3月前
|
算法 Java
LeetCode经典算法题:矩阵中省份数量经典题目+三角形最大周长java多种解法详解
LeetCode经典算法题:矩阵中省份数量经典题目+三角形最大周长java多种解法详解
52 6
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 30. 包含min函数的栈
本文提供了实现一个包含min函数的栈的Python代码,确保min、push和pop操作的时间复杂度为O(1)。
28 4