【CSDN 编者按】极大概率出现在面试中的约瑟夫环问题来啦,本文三种方法描述解题思路,这样讲解绝对让面试官眼前一亮。

作者 | bigsai 责编 | 欧阳姝黎

前言

约瑟夫环问题是算法中相当经典的一个问题,其问题理解是相当容易的,并且问题描述有非常多的版本,并且约瑟夫环问题还有很多变形,这篇约瑟夫问题的讲解,一定可以带你理解通通!

什么是约瑟夫环问题?

约瑟夫环问题在不同平台被"优化"描述的不一样,例如在牛客剑指 offer 叫孩子们的游戏,还有叫杀人游戏,点名……最直接的感觉还是力扣上剑指 offer62 的描述:圆圈中最后剩下的数字。

问题描述:

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

当然,这里考虑 m,n 都是正常的数据范围,其中

1 <= n <= 10^5

1 <= m <= 10^6

对于这个问题,你可能脑海中有了印象,想着小时候村里一群孩子坐在一起,从某个开始报数然后数到几出列,下一个重新开始一直到最后一个。不同人用不同方法解决,青铜直接模拟,钻石会优化一下,王者用公式,下面详细给大家讲解思路。

循环链表模拟

这个问题最本质其实就是循环链表的问题,围成一个圈之后,就没有结尾这就是一个典型的循环链表嘛!一个一个顺序报数,那不就是链表的遍历枚举嘛!数到对应数字的出列,这不就是循环链表的删除嘛!

链表模拟

并且这里还有非常方便的地方:

循环链表的向下枚举不需要考虑头尾问题,直接 node=node.next 向下

循环链表的删除也不需要考虑头尾问题,直接 node.next=node.next.next 删除

当然也有一些需要注意的地方

形成环形链表很简单,只需要将普通链表的最后一个节点的 next 指向第一个节点即可

循环链表中只有一个节点的时候停止返回,即 node.next=node 的时候

删除,需要找到待删除的前面节点,所以我们删除计数的时候要少计一位,利用前面的那个节点直接删除后面节点即可

这样,思路明确,直接开撸代码:

class Solution {

class node//链表节点

int val;

public node(int value) {

this.val=value;

node next;

public int lastRemaining(int n, int m) {

if(m==1)return n-1;//一次一个直接返回最后一个即可

node head=new node(0);

node team=head;//创建一个链表

for(int i=1;i {

team.next=new node(i);

team=team.next;

team.next=head;//使形成环

int index=0;//从0开始计数

while (head.next!=head) {//当剩余节点不止一个的时候

//如果index=m-2 那就说明下个节点(m-1)该删除了

if(index==m-2)

head.next=head.next.next;

index=0;

else {

index++;

head=head.next;

return head.val;

当然,这种算法太复杂了,大部分的 OJ 你提交上去是无法 AC的,因为超时太严重了,具体的我们可以下面分析。

有序集合模拟

上面使用链表直接模拟游戏过程会造成非常严重非常严重的超时,n 个数字,数到第 m 个出列。因为m如果非常大远远大于 n,那么将进行很多次转圈圈。

所以我们可以利用求余的方法判断等价最低的枚举次数,然后将其删除即可,在这里你可以继续使用自建链表去模拟,上面的 while 循环以及上面只需添加一个记录长度的每次求余算圈数即可:

int len=n;

while (head.next!=head) {

if(index==(m-2)%len)

head.next=head.next.next;

index=0;

len--;

else {

index++;

head=head.next;

但我们很多时候不会手动去写一个链表模拟,我们会借助 ArrayList 和 LinkedList 去模拟,如果使用 LinkedList 其底层也是链表,使用 ArrayList 的话其底层数据结构是数组。不过在使用 List 其代码方法一致。

List 可以直接知道长度,也可删除元素,使用 List 的难点是一个顺序表怎么模拟成循环链表?

咱们仔细思考:假设当前长度为 n,数到第 m 个(通过上面分析可以求余让这个有效的 m%n 不大于 n)删除,在 index 位置删除。那么删除后剩下的就是 n-1长度,index 位置就是表示第一个计数的位置,我们可以通过求余得知走下一个删除需要多少步,那么下个位置怎么确定呢?

删除3号下标

你可以分类讨论看看走的次数是否越界,但这里有更巧妙的方法,可以直接求的下一次具体的位置,公式就是为:

index=(index+m-1)%(list.size());

因为 index 是从 1 计数,如果是循环的再往前 m-1 个就是真正的位置,但是这里可以先假设先将这个有序集合的长度扩大若干倍,然后从 index 计数开始找到假设不循环的位置 index2,最后我们将这个位置 index2%(集合长度)即为真正的长度。

真实位置计算

使用这个公式一举几得,既能把上面m过大循环过多的情况解决,又能找到真实的位置,就是将这个环先假设成线性的然后再去找到真的位置,如果不理解的话可以再看看这个图:

这种情况的话大部分的 OJ 是可以勉强过关的,面试官的层面也大概率差不多的,具体代码为:

class Solution {

public int lastRemaining(int n, int m) {

if(m==1)

return n-1;

Listlist=new ArrayList<>();

for(int i=0;i {

list.add(i);

int index=0;

while (list.size()>1)

index=(index+m-1)%(list.size());

list.remove(index);

return list.get(0);

递归公式解决

我们回顾上面的优化过程,上面用求余可以解决 m 比 n 大很多很多的情况(即理论上需要转很多很多圈的情况)。但是还可能存在 n 本身就很大的情况,无论是顺序表 ArrayList 还是链表 LinkedList 去频繁查询、删除都是很低效的。

所以聪明的人就开始从数据找一些规律或者关系。

先抛出公式:

f(n,m)=(f(n-1,m)+m)%n

f(n,m)指n个人,报第m个编号出列最终编号

下面要认真看一下我的分析过程:

我们举个例子,有0 1 2 3 4 5 6 7 8 9 十个数字,假设 m 为 3,最后结果可以先记成f(10,3),即使我们不知道它是多少。

当进行第一次时候,找到元素 2 删除,此时还剩 9 个元素,但起始位置已经变成元素 3。等价成3 4 5 6 7 8 9 0 1这 9 个数字重写开始找。

f(10,3)删除第一个数

此时这个序列最终剩下的一个值即为f(10,3),这个序列的值和 f(9,3)不同,但是都是 9 个数且 m 等于 3,所以其删除位置是相同的,即算法大体流程是一致的,只是各位置上的数字不一样。所以我们需要做的事情是找找这个序列上和f(9,3)值上有没有什么联系。

寻找过程中别忘记两点,首先可通过%符号对数字有效扩充,即我们可以将 3 4 5 6 7 8 9 0 1这个序列看成(3,4,5,6,7,8,9,10,11)%10.这里的10即为此时的n数值。

另外数值如果是连续的,那么最终一个结果的话是可以找到联系的(差值为一个定制)。所以我们可以就找到f(10,3)和f(9,3)值之间结果的关系,可以看下图:

f(10,3)删除一次和f(9,3)

所以f(10,3)的结果就可以转化为f(9,3)的表达,后面也是同理:

f(10,3)=(f(9,3)+3)%10

f(9,3)=(f(8,3)+3)%9

f(2,3)=(f(1,3)+3)%2

f(1,3)=0

这样,我们就不用模拟操作,可以直接从数值的关系找到递推的关系,可以轻轻松松的写下代码:

class Solution {

int index=0;

public int lastRemaining(int n, int m) {

if(n==1)

return 0;

return (lastRemaining(n-1,m)+m)%n;

但是递归效率因为有个来回的规程,效率相比直接迭代差一些,也可从前往后迭代:

class Solution {

public int lastRemaining(int n, int m) {

int value=0;

for(int i=1;i<=n;i++)

value=(value+m)%i;

return value;

结语

我想,通过本篇文章你应该掌握和理解了约瑟夫环问题,这种裸的约瑟夫环问题出现的概率很大,考察很频繁,链表模拟是根本思想,有序集合模拟链表是提升,而公式递推才是最有学习价值的地方,如果你刚开始接触不理解可以多看几遍。如果能用公式递推给面试官说两句,讲讲原理,那一定会让面试官眼前一亮的!

4月20日晚八点,欢迎来到CSDN悦读时间直播间,与四位大咖一起探索UNIX传奇往事的启示,围观《UNIX传奇》新书发布会!

?王兴评华为造车:技术实力、忽悠能力都和特斯拉旗鼓相当;“微信键盘”开启内测;PDF 开发者去世|极客头条?6 岁就成“大厂团宠”,这门编程语言竟引 Linux、谷歌、亚马逊共“折腰” !?Mitchell Baker:担任 Mozilla CEO 是我最艰难的职业

约瑟夫环c语言单链表的解题思路,太透彻了:约瑟夫环的三种解法相关推荐

  1. 约瑟夫环c语言代码 指针,约瑟夫环C语言实现源代码(1)

    前天笔试有个约瑟夫环的问题,怪不得人家没通知我面试,原来我的约瑟夫环做的确实有问题,昨天晚上又重新做了下,下面上源代码: /* file:osephu.cpp author:www.5dkx.com ...

  2. 约瑟夫环c语言出现段错误,算法竞赛入门经典 紫书 第四章

    一点小问题 关于判断素数的几点 //该函数有严重缺点: //不能用于n==1和n较大的情况 //在n接近int的最大值时: //若i=46340时,i*i=2147395600//若i=46341时, ...

  3. 约瑟夫环c语言代码顺序存储,顺序表实现约瑟夫环地问题,C语言.doc

    顺序表实现约瑟夫环地问题,C语言 计算机科学与工程学院 PAGE PAGE 2 <算法与数据结构>试验报告 计算机科学与工程学院 <算法与数据结构>试验报告[一] 专业班级 1 ...

  4. 约瑟夫环c语言计蒜客链表,约瑟夫环的故事 - osc_3n35hvex的个人空间 - OSCHINA - 中文开源技术交流社区...

    usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading. ...

  5. 约瑟夫环c语言程序完整版,C语言:约瑟夫环问题(源代码)

    本帖最后由 geige 于 2015-7-26 00:48 编辑 #include #include struct stu //构建一个新的数据类型 { int num; struct stu *ne ...

  6. 用C语言编写约瑟夫环程序,约瑟夫环C语言,请高手检查我的程序

    /*TC2编译通过*//*测试了几组数据,没有发现问题*//*如果有问题,再M我*/#include typedef struct Cnode {int data; int password; str ...

  7. 约瑟夫环c语言循环指针,约瑟夫环(c语言)(双循环、单循环)

    /*题目: 耶稣又15个门徒,其中有一个时出卖耶稣的叛徒,请用排除法找出这位门徒:15人围坐一圈,从第一个开始报号:1,2,3,1,2,3...凡是报到"3"的退出圈子,最后留在圈 ...

  8. C语言一趟冒泡交换最小值,C语言单链表冒泡排序为啥以下代码实现不了?

    struct node *sort(struct node *head)/*排序*/ { struct node *p,*q; struct node *temp; for(p=head;p!=NUL ...

  9. 约瑟夫环数据结构c语言程序,数据结构的C语言(类C语言)--单向循环链表--约瑟夫环...

    代码区 约瑟夫环:用类C语言实现!!!可以成功运行!!!不是仅仅的算法,而是实实在在的类C #include #include typedef int ElemType; typedef struct ...

最新文章

  1. java统计多个线程的请求次数_Web并发页面访问量统计实现
  2. java视频流传输_java – 使用Xuggler流式传输视频
  3. Spring+Mybatis多数据源配置(三)——Spring如何获取Properties文件的信息
  4. 接口测试--获取动态参数进阶
  5. HDU1263 水果【map】
  6. Codeforces.1051G.Distinctification(线段树合并 并查集)
  7. MVVM设计模式《网摘》
  8. html中src为空,怎么解决img里面src为空状态下的边框问题
  9. 5款最好用的前端开发工具
  10. python中jieba库安装中出现pip库需要更新怎么办_python安装jieba库
  11. 【9】数据可视化:基于 Echarts + Python 实现的动态实时大屏 - 酒店行业
  12. Android 常用框架大全
  13. 从小就对生意耳濡目染的小伙,长大后创业资产过亿
  14. 【Rust日报】2020-01-27 QIP:Rust中的量子计算模拟
  15. 三星健身服务器无响应 怎么办,三星携手UA健身APP、强化创新型可穿戴设备
  16. 华为面试题: 杨辉三角形的变形
  17. 做人呢,最重要的就是开心啦~
  18. 解析dump的几种方式
  19. 微信小程序毕业设计 基于微信体育馆场地预约小程序系统开题报告
  20. 云服务卸载MySQL

热门文章

  1. Windows配置网络
  2. -markdown编辑器
  3. matlab 古典概率求解,第1章数学建模古典概型解答.ppt
  4. Nodejs 中 request 出现 ‘socket hang up‘ 的解决办法
  5. 大连暗泉渗透/红队岗面试题(高级渗透测试工程师面试题)总结
  6. 网易云易盾三款产品入选2018网络安全全景图
  7. OC5228 100V多功能LED恒流驱动器-高辉调光 65536:1 调光比
  8. CI环境搭建-创建git
  9. php服务器能运行java吗_将PHP与Java服务器接口
  10. Shader学习之Cg语言一(Cg语言概述)