算法 - 剑指Offer 圆圈中最后剩下的数字

题目

0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。

解题思路

这题很难,需要推导公式, 首先我们按照一步一步来,从n=5, m=3来做删除工作,直到留下最后的元素d, 所以我们最后的返回一定是元素d所在的下标, 那么我们是否可以获得每次删除后元素d所在的位置呢,请看下标变化的具体流程, 其中第二行是没有变化的下标,让他一直叠加,后面是给定的下标,也就是注释中第一部分的下标,f(n,m)返回是元素d所在的下标, 那么f(n, m) = (f(n-1, m) + m%n)%n = (f(n-1, m) + m)%n,注 m%n%n = m%n 可以得出这样的公式,公式解释如下首先如何获得删除的元素下标, 如果是删除第m个元素,其实被删除的就是m-1下标的元素,保留的就是m, 因为是闭环,m可以打过n, 所以这里使用m%n, 然后每次迭代删除一个元素, 元素就会减少1个, 所以这里的f(n-1, m)表示上一个元素所在的位置,再加上前面的m%n,在对其%n这个就是我们最后元素所在的位置了. 类似于迭代了, 我们还知道的一点是, 最后就剩一个元素了,其下标必为0,那f(1,m)=0,无论m是多少,然后我们就可以逆向推导上去一直到我们要的f(5,3)的结果了, 具体代码如下。

Java代码实现

public class LastRemaining {
    /**
     * 约瑟夫环问题
     * n = 5, m = 3
     * 第1次循环
     * a b c d e
     * 0 1 2 3 4
     * 第2次循环
     * d e a b
     * 0 1 2 3
     * 第3次循环
     * b d e
     * 0 1 2
     * 第4次循环
     * b d
     * 0 1
     * 第5次循环
     * d
     * 0
     *
     * 第2次开始下标开始变化,f(4,3) 原始变化如下
     * --m-- d e a b (d e)
     * 0 1 2 3 4 5 6 7 8
     *       0 1 2 3 4 5
     * 第3次再次原始变化如下:
     * --m-- --m-- b d e (b d)
     * 0 1 2 3 4 5 6 7 8 9 10
     *             0 1 2 3 4
     * 第4次再次原始变化如下:
     * --m-- --m-- --m-- b d  (b  d)
     * 0 1 2 3 4 5 6 7 8 9 10 (11 12)
     *                   0 1
     * 第5次再次原始变化如下:
     * --m-- --m-- --m-- ---m--- d
     * 0 1 2 3 4 5 6 7 8 9 10 11 12
     *                           0
     * f(n=5,m=3) 表示5个元素,删除第三个元素, 表示最后剩余元素d所有在的位置
     * f(5,3) = (f(4,3) + m) % n = 3 % 5 = 3
     * f(4,3) = (f(3,3) + m) % n = 4 % 4 = 0
     * f(3,3) = (f(2,3) + m) % n = 4 % 3 = 1
     * f(2,3) = (f(1,3) + m) % n = 3 % 2 = 1
     * f(1,3) = (f(0,3) + m) % n = (0 + 3) % 1 = 0
     */
    public int lastRemaining(int n, int m) {
        return f(n, m);
    }
    private int f(int n, int m) {
        if(n == 1){
            return 0;
        }
        int x = f(n - 1, m);
        return  (m+x)%n;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值