当前位置: 首页 > news >正文

剑指Offer62 -- 约瑟夫环

1. 题目描述

圆圈中最后剩下的数字



2. 约瑟夫环

人们站在一个等待被处决的圈子里。 计数从圆圈中的指定点开始,并沿指定方向围绕圆圈进行。 在跳过指定数量的人之后,处刑下一个人。 对剩下的人重复该过程,从下一个人开始,朝同一方向跳过相同数量的人,直到只剩下一个人,并被释放。



3. 思路

下面主要是对参考题解的补充说明。
现在正式开始😊

我们现在假设有一个长度为 n n n 的数组 a a a,数组下标从 0 0 0 开始,每数 m m m 个数就删除当前位置的元素,直到只剩下一个元素。第一次删除 a [ k ] a[k] a[k], k = ( m − 1 ) % n k=(m-1)\%n k=(m1)%n(考虑 m > n m>n m>n的情况),那么删除 a [ k ] a[k] a[k],之后,原数组就变成了:
a [ k + 1 ] , a [ k + 2 ] , . . . , a [ n − 1 ] , a [ 0 ] , a [ 1 ] , . . . a [ k − 1 ] a[k + 1], a[k + 2], ... , a[n - 1], a[0], a[1], ... a[k - 1] a[k+1],a[k+2],...,a[n1],a[0],a[1],...a[k1](到了头就重新从下标 0 0 0 处开始)
仔细看的话,这里,我们没有标明 a [ k ] a[k] a[k],说明它的确被删除了。
哎,你可能会问,怎么可能真的把它删除了啊(况且代码中确实没有“删除”动作),我们在模拟的时候,对于“删除”的元素,都是跳过,既然都跳过了,说明它的的确确真真实实的存在啊!
错了!此删除非彼删除!先别急!
现在,我们删除了一个元素,新数组就是上面的样子,我们需要删除第二个元素,但是,他的位置不是 k = ( m − 1 ) % n k=(m-1)\%n k=(m1)%n 了,而是 k = ( m − 1 ) % ( n − 1 ) k=(m-1)\%(n-1) k=(m1)%(n1)
删除位置的变化非常非常关键!我们通过缩减数组的大小,使得我们通过忽略 a [ k ] a[k] a[k] 以达到删除 a [ k ] a[k] a[k] 的目的,因为 a [ k ] a[k] a[k] 如果存在的话,它就在 a [ k − 1 ] a[k-1] a[k1] 的后面,也就是长度为 n n n 的数组的最后一个元素,但是现在数组长度为 n − 1 n-1 n1 了,所以 a [ k ] a[k] a[k] 不就相当于被删除了吗?
往下同理,当我们删除了 c n t cnt cnt 个元素之后, k = ( m − 1 ) % ( n − c n t ) k=(m-1)\%(n-cnt) k=(m1)%(ncnt)

好了,我们已经解释完了,通过什么样的方法来实现模拟过程中跳过被删除的元素,就是把它放到数组末尾,然后删除。
但是!问题来了!虽然说,我们可以找到 k k k 的位置,但是你没办法把数组 a a a 转换为下面形式:
删除一个元素之后的新数组: a [ k + 1 ] , a [ k + 2 ] , . . . , a [ n − 1 ] , a [ 0 ] , a [ 1 ] , . . . a [ k − 1 ] a[k + 1], a[k + 2], ... , a[n - 1], a[0], a[1], ... a[k - 1] a[k+1],a[k+2],...,a[n1],a[0],a[1],...a[k1]
因为, a [ 0 ] a[0] a[0] 就是 a [ 0 ] a[0] a[0] a [ k + 1 ] a[k+1] a[k+1] 就是 a [ k + 1 ] a[k+1] a[k+1],你说 原数组中的 a [ k + 1 ] a[k+1] a[k+1] 相当于 新数组的 a [ 0 ] a[0] a[0],他就是新数组中的 a [ 0 ] a[0] a[0] 啊?

确实,我们假象,删除一个元素后,新数组的头是原数组的 a [ k + 1 ] a[k+1] a[k+1],但是事实是残酷的,数组的头仍然是原数组的 a [ 0 ] a[0] a[0]
那么,有问题就解决问题,怎么办呢?最简单的办法,用一个 f o r for for 循环移动元素,让 a [ 0 ] a[0] a[0] 真的等于 a [ k + 1 ] a[k+1] a[k+1]
但这也太笨了! o f f e r offer offer 是留给不走寻常路之人的!
我们可以通过映射大法,虚假的让原数组的 a [ k + 1 ] a[k+1] a[k+1] 等于新数组的 a [ 0 ] a[0] a[0],因为新数组的理论下标是连续的(从 0 0 0 n − 2 n-2 n2
我们指望被映射的下标也是连续的(取模意义下): k + 1 , k + 2 , . . . n − 1 , 0 , 1 , . . . k − 1 k+1, k+2, ... n-1, 0, 1, ...k-1 k+1,k+2,...n1,0,1,...k1
既然两个数组都是连续的,我们当然可以将我们希望的数组下标(从 k + 1 k+1 k+1 开始的)映射到实际的数组下标(从 0 0 0 开始的)了!

接下来映射部分参考题解就好了!

总之,思路很简单!只需要搞清楚两个问题:

  1. 如果模拟删除行为
  2. 如何将被删除元素的下一个位置开始的元素作为数组的第一个元素

另外,你可能会问为啥我们必须要映射呢?我们设置一个变量 s t a r t I n d e x = k + 1 startIndex=k+1 startIndex=k+1,然后让他递增(取模状态下)不久好了?这样不也能实现映射的行为吗?
也就与我们递归的意义有关了。
我们的递归函数 l a s t R e m a i n i n g ( i n t n , i n t m ) lastRemaining(int n, int m) lastRemaining(intn,intm) 表示,删除从 0 0 0 n − 1 n-1 n1 一共 n n n 个元素中的 m m m 个数,最后剩下的数。
他的第一个数就是 0 0 0,最后一个数就是 n − 1 n-1 n1。。。



4. 代码

class Solution {
public:
    int lastRemaining(int n, int m) {
        if(n == 1)  return 0;
        return (lastRemaining(n - 1, m) + m) % n;
    }   
};


5. 参考

数学推理–映射

6. 其它题目

求出队序列

相关文章:

  • 黑盒测试的概念和特点
  • JAVA SE :认识数组
  • C#中,什么是委托,什么是事件及它们之间的关系
  • Linux内核调试 - Hung_task机制分析下
  • ADZS-ICE-2000和AD-ICE2000仿真器在线升级固件
  • 典范硬币系统(Canonical Coin System)→ 贪心算法
  • EXCEL报错:无法共享此工作薄,因表包含excel表或xml映射的解决方法
  • 合合信息TextIn大模型加速器 2.0来了:智能文档解析和图表解析能力全面升级
  • 深入理解 Linux 文件权限:从 ACL 到扩展属性,解剖底层技术细节与命令应用
  • Selenium三大等待
  • Transformer-LSTM、Transformer、CNN-LSTM、LSTM、CNN五模型多变量回归预测
  • LeetCode349两个数组的交集
  • uvm transaction
  • 排查使用RestTemplate远程调用,@RequestBody注解接收不到实体类
  • python面试
  • 【天梯赛】L2-004 这是二叉搜索树吗(经典问题C++)
  • 笔试专题(四)
  • Ollama及HuggingFace路径环境变量设置
  • 监控易一体化运维:监控易机房管理,打造高效智能机房
  • 51c嵌入式~三极管~合集1
  • tklink的登录做网站/seo入门书籍推荐
  • 一个主机可以建设多少个网站/网站流量统计查询
  • 厦门市工程建设项目网上办事大厅/关键词搜索优化
  • 华为手机开发者模式怎么关闭/文山seo公司
  • 阿里巴巴怎么做网站/sem优化和seo的区别
  • 南昌网站设计公司/贷款客户大数据精准获客