剑指 Offer 62. 圆圈中最后剩下的数字(leetcode)--约瑟夫问题

2023-11-06

题目描述:

在这里插入图片描述

算法 :约瑟夫问题

算法描述:

约瑟夫问题是个著名的问题:N个人围成一圈,第一个人从1开始报数,报M的将被杀掉,下一个人接着从1开始报。如此反复,最后剩下一个,求最后的胜利者。
例如只有三个人,把他们叫做A、B、C,他们围成一圈,从A开始报数,假设报2的人被杀掉。

  • 首先A开始报数,他报1。侥幸逃过一劫。
  • 然后轮到B报数,他报2。非常惨,他被杀了
  • C接着从1开始报数
  • 接着轮到A报数,他报2。也被杀死了。
  • 最终胜利者是C

解决方案

普通解法

刚学数据结构的时候,我们可能用链表的方法去模拟这个过程,N个人看作是N个链表节点,节点1指向节点2,节点2指向节点3,……,节点N-1指向节点N,节点N指向节点1,这样就形成了一个环。然后从节点1开始1、2、3……往下报数,每报到M,就把那个节点从环上删除。下一个节点接着从1开始报数。最终链表仅剩一个节点。它就是最终的胜利者。

在这里插入图片描述

缺点:

要模拟整个游戏过程,时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。

公式法

约瑟夫环是一个经典的数学问题,我们不难发现这样的依次报数,似乎有规律可循。为了方便导出递推式,我们重新定义一下题目。

问题: N个人编号为1,2,……,N,依次报数,每报到M时,杀掉那个人,求最后胜利者的编号。

这边我们先把结论抛出了。之后带领大家一步一步的理解这个公式是什么来的。
递推公式:

  			**f(N,M)=(f(N−1,M)+M)%N**
  • f(N,M)表示,N个人报数,每报到M时杀掉那个人,最终胜利者的编号

  • f ( N − 1 , M ) f(N-1,M)f(N−1,M)表示,N-1个人报数,每报到M时杀掉那个人,最终胜利者的编号

下面我们不用字母表示每一个人,而用数字。

1、2、3、4、5、6、7、8、9、10、11

表示11个人,他们先排成一排,假设每报到3的人被杀掉。

  • 刚开始时,头一个人编号是1,从他开始报数,第一轮被杀掉的是编号3的人。
  • 编号4的人从1开始重新报数,这时候我们可以认为编号4这个人是队伍的头。第二轮被杀掉的是编号6的人。
  • 编号7的人开始重新报数,这时候我们可以认为编号7这个人是队伍的头。第三轮被杀掉的是编号9的人。
  • ……
  • 第九轮时,编号2的人开始重新报数,这时候我们可以认为编号2这个人是队伍的头。这轮被杀掉的是编号8的人。
  • 下一个人还是编号为2的人,他从1开始报数,不幸的是他在这轮被杀掉了。
  • 最后的胜利者是编号为7的人。

在这里插入图片描述
现在再来看我们递推公式是怎么得到的!
将上面表格的每一行看成数组,这个公式描述的是:幸存者在这一轮的下标位置

  • f(1,3):只有1个人了,那个人就是获胜者,他的下标位置是0

  • f ( 2 , 3 ) = ( f ( 1 , 3 ) + 3 ) % 2 = 3 % 2 = 1 :在有2个人的时候,胜利者的下标位置为1

  • f ( 3 , 3 ) = ( f ( 2 , 3 ) + 3 ) % 3 = 4 % 3 = 1 :在有3个人的时候,胜利者的下标位置为1

  • f ( 4 , 3 ) = ( f ( 3 , 3 ) + 3 ) % 4 = 4 % 4 = 0 :在有4个人的时候,胜利者的下标位置为0

  • ……

  • f ( 11 , 3 ) = 6

很神奇吧!现在你还怀疑这个公式的正确性吗?上面这个例子验证了这个递推公式的确可以计算出胜利者的下标,下面将讲解怎么推导这个公式。

  1. 问题1: 假设我们已经知道11个人时,胜利者的下标位置为6。那下一轮10个人时,胜利者的下标位置为多少?

    答: 其实吧,第一轮删掉编号为3的人后,之后的人都往前面移动了3位,胜利这也往前移动了3位,所以他的下标位置由6变成3。

  2. 假设我们已经知道10个人时,胜利者的下标位置为3。那下一轮11个人时,胜利者的下标位置为多少?

    答:这可以看错是上一个问题的逆过程,大家都往后移动3位,所以f ( 11 , 3 ) = f ( 10 , 3 ) + 3 。不过有可能数组会越界,所以最后模上当前人数的个数,f ( 11 , 3 ) = ( f ( 10 , 3 ) + 3 ) % 11

  3. :现在改为人数改为N,报到M时,把那个人杀掉,那么数组是怎么移动的?

    答:每杀掉一个人,下一个人成为头,相当于把数组向前移动M位。若已知N-1个人时,胜利者的下标位置位f ( N − 1 , M ) f(N-1,M)f(N−1,M),则N个人的时候,就是往后移动M为,(因为有可能数组越界,超过的部分会被接到头上,所以还要模N),既f ( N , M ) = ( f ( N − 1 , M ) + M ) % n

注:理解这个递推式的核心在于关注胜利者的下标位置是怎么变的。每杀掉一个人,其实就是把这个数组向前移动了M位。然后逆过来,就可以得到这个递推式。

参考:约瑟夫环——公式法(递推公式)

代码

下面给出代码实现:

public static int lastRemaining(int n, int m) {
		int p = 0;
		for(int i=2;i<=n;i++)
		{
			p=(p+m)%i;
		}
		return p;
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

剑指 Offer 62. 圆圈中最后剩下的数字(leetcode)--约瑟夫问题 的相关文章

随机推荐

  • Ant Design Pro使用技巧之mock数据地址改为服务器地址

    Ant Design Pro本身提供了较为强大的mock数据的功能 然而 当如果是单人开发模式或者后台已经开发完成的情况下 我们更希望在前端开发调试过程中直接访问后端服务的接口 本文主要讲述该技巧 即如何将mock数据地址改为服务器地址 r
  • 晶振的基本原理

    晶振的全称 石英晶体谐振器 1 晶振原理 将石英晶体按一定的方位角切割成不同形状 在两个对立面上涂覆银层作为电极 在每个电机上焊接引线作为管脚 再用外壳封装即为晶振 切割的定位角与最后的成型形状决定了晶振的振动频率 切割精度完了晶振的振动精
  • 说的很不错的关于程序员文章。

    关键是 关注心灵 关注自己 这能让你成为一个更好的程序员 你可以无止境的学习新语法 新工具 或新什么东西 但是 如果所有你做的只是编程 你 实际上在跟自己背道而驰 有时候你需要全力以赴 但那是当程序中有问题需要救火时 是特殊情况 而不是日常
  • 012 python数据结构与算法:链表

    链表 为什么需要链表 顺序表的构建需要预先知道数据大小来申请连续的存储空间 而在进行扩充时有需要进行数据的搬迁 所以使用起来并不是很灵活 链表结构可以充分的利用计算机内存空间 实现灵活的内存动态管理 链表的定义 链表 Linked list
  • 程序员推荐简单有效的科学健脑方法

    勤练脑力可使记忆力增强 勤做有氧运动可使大脑灰质增加 勤于思考可使理智与情感有机互补 这些措施看上去很美 但美中不足的是 它们对大脑的训练都不够彻底 这也是越来越多此类研究的通病 记忆训练对大脑的好处当然比看真人秀什么的要靠谱得多 但这些训
  • 基于C++标准模板库(STL)的sort排序函数,超实用介绍!!!

    sort函数 简介 顾名思义 sort就是用来排序的函数 它可以根据具体情形进行自动或人为使用不同得排序方法 接下来希望通过这篇介绍来帮助读者们轻松愉快地使用sort函数 1 如何使用sort排序 使用条件 sort函数的使用必须加上头文件
  • 扫描流——Scanner类

    BufferedReader类方便了对大文本数据文件的读取操作 但是它存在两个问题 读取数据的时候只能按照字符串返回 public String readLine throws IOException 分隔符是固定的 以换行作为分隔符 于是
  • Attention+GRU

    数据集 纳斯达克100 模型原理 模型代码 class Attention Layer def init self step dim W regularizer None b regularizer None W constraint No
  • 软件开发人员的作战手册 - 让程序员活的久一点

    1 程序员的职业准则是 诚实 如实的报告你的状态 风险和出现的问题 守信 承诺完成的任务就要按时完成 尊重 尊重给你的代码提建议的同事 对事不对人 2 写有BUG 的代码和写没有 BUG 的代码花费的时间是一样的 3 BUG是会成长的 存活
  • vue中本地静态图片的路径应该如何写

    需求 如何components里面的index vue怎样能把assets里面的图片拿出来 1 在img标签里面直接写上路径 img src assets a1 png class width 100 2 利用数组保存再循环输出
  • spring boot配置tomcat部署(12.24修改)

    spring boot本身默认为jar包运行 可以改为war包 然后运行在tomcat里 具体修改的步骤如下 1 在pom xml文件里添加需要的依赖
  • Read keyboad input from background process

    https raspberrypi stackexchange com questions 55431 read keyboad input from background process TIPS For a new project I
  • 9、利用Maven的Source插件,对Maven工程的源码进行打jar包

    在很多情况下 需要对于Maven工程的源代码进行源文件的打包 可以利用source插件来完成 利用Maven的Source插件 对Maven工程的源码进行打jar包 1 新建一个Maven项目 如下 2 对于source插件的简介如下 1
  • freemarker教程

    FreeMarker语言 FreeMarker语言概述 FreeMarker是一个模板引擎 一个基于模板生成文本输出的通用工具 使用纯Java编写 FreeMarker被设计用来生成HTML Web页面 特别是基于MVC模式的应用程序 虽然
  • 高级计算机网络 知识点总结

    高级计算机网络知识点总结 一 引言 一 OSI七层模型 OSI定义了网络互连的七层框架 物理层 数据链路层 网络层 传输层 会话层 表示层 应用层 即ISO开放互连系统参考模型 每一层实现各自的功能和协议 并完成与相邻层的接口通信 OSI的
  • 5G及移动边缘计算(MEC)学习笔记(1)

    原文链接 https blog csdn net gongxifacai believe article details 80804841 1 1G 5G发展变革 1G 第一代移动通信系统出现在蜂窝系统理论提出之后 主要满足人们无线移动通话
  • GameFi 增长: 如果保持游戏用户的留存

    Mar 2023 Daniel 链游存在用户留存低的问题 对于所有关于成为游戏的未来的讨论 90 的区块链游戏在30天内就不活跃了 如果没有玩家长期享受游戏 今天大多数GameFi项目仍然只是DeFi协议 以及有更漂亮的图形和一些互动元素
  • idea 安装 Vue 插件后没有新建Vue文件Vue component选项

    解决办法 2 copy之后会出现一个新文件 Name 改成 Vue Component 然后把代码里的 COMPONENT 删掉即可
  • Android高斯模糊(毛玻璃效果)蒙层库

    ShapeBlurView ShapeBlurView库是一个高斯模糊 毛玻璃效果 蒙层库 Like iOS UIVisualEffectView不知大家做需求的时候是否有这样的效果要求 需求示例 大家熟悉的Android常用图片加载库 比
  • 剑指 Offer 62. 圆圈中最后剩下的数字(leetcode)--约瑟夫问题

    文章目录 题目描述 算法 约瑟夫问题 算法描述 解决方案 普通解法 缺点 公式法 代码 题目描述 算法 约瑟夫问题 算法描述 约瑟夫问题是个著名的问题 N个人围成一圈 第一个人从1开始报数 报M的将被杀掉 下一个人接着从1开始报 如此反复