关于剑指offer2中62题关于约瑟夫环问题的记录

约瑟夫环问题

看了许多帖子,只有约瑟夫环这篇文章,讲的比较清楚,但还是想用简单的方式在此做个记录,方便自己下次回顾。

问题:

假设给定数组n,数组中的元素依次排序排成了一个圆,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。

假设数组n,其元素如下所示:每次从这个数组里删除第3个数字,求最后剩下来的数字是几?
我们先给出最后剩下来的数字,答案为4,方便我们后续观察规律。
在这里插入图片描述
在第一轮中,我们可以看到需要剔除的第三个数字是3,同时最后剩下的数字4的下标是3。
在这里插入图片描述
在第二轮中,我们可以看到需要剔除的第3个数字是6,同时最后剩下的数字4的下标是0。
在这里插入图片描述
在第三轮中,我们可以看到需要剔除的第3个数字是2,同时最后剩下的数字4的下标是3。
在这里插入图片描述
在第四轮中,我们可以看到需要剔除的第3个数字是7,同时最后剩下的数字4的下标是0。
在这里插入图片描述

在第五轮中,我们可以看到需要剔除的第3个数字是5,同时最后剩下的数字4的下标是1。
在这里插入图片描述
在第六轮中,我们可以看到需要剔除的第3个数字是1,同时最后剩下的数字4的下标是1。
在这里插入图片描述
第七轮就只剩下4一个数字了。4就是我们要求的数字了。


问题1:通过这种排列方式我们可以得到什么规律?

为方便观察,将总图附上:
在这里插入图片描述
观察上图:我们可以发现,以这种排序的方式。无论数组中有多少个元素,最终我们得到的剩下来的数字 ,它的下标都为0。这一点很重要!

我们设 f ( f( f(N,m)表示,当数组元素的个数为N时,剔除第m位置的数字后,最终剩下来的数字的下标位置。
观察上图,可以发现每一轮中数字4的下标可以用该式表示为:
第1轮: f ( 7 , 3 ) = 3 f(7,3) = 3 f(7,3)=3
第2轮: f ( 6 , 3 ) = 0 f(6,3) = 0 f(6,3)=0
第3轮: f ( 5 , 3 ) = 3 f(5,3) = 3 f(5,3)=3
第4轮: f ( 4 , 3 ) = 0 f(4,3) = 0 f(4,3)=0
第5轮: f ( 3 , 3 ) = 1 f(3,3) = 1 f(3,3)=1
第6轮: f ( 2 , 3 ) = 1 f(2,3) = 1 f(2,3)=1
第7轮: f ( 1 , 3 ) = 0 f(1,3)= 0 f(1,3)=0
通过顺序递推,我们现在才真正得到了数组n中,最终剩下来的数字为4,且其下标为0,现在我们 进行逆向推导:
第1轮: f ( 1 , 3 ) = 0 f(1,3)= 0 f(1,3)=0
第2轮: f ( 2 , 3 ) = ( f ( 1 , 3 ) + 3 ) f(2,3) = (f(1,3) + 3) f(2,3)=(f(1,3)+3) % 2 = 1 2= 1 2=1
第3轮: f ( 3 , 3 ) = ( f ( 2 , 3 ) + 3 ) f(3,3) = (f(2,3) + 3) f(3,3)=(f(2,3)+3) % 3 = 1 3 = 1 3=1
第4轮: f ( 4 , 3 ) = ( f ( 3 , 3 ) + 3 ) f(4,3) = (f(3,3) + 3) f(4,3)=(f(3,3)+3) % 4 = 0 4 = 0 4=0
第5轮: f ( 5 , 3 ) = ( f ( 4 , 3 ) + 3 ) f(5,3) = (f(4,3) + 3) f(5,3)=(f(4,3)+3) % 5 = 3 5 = 3 5=3
第6轮: f ( 6 , 3 ) = ( f ( 5 , 3 ) + 3 ) f(6,3) = (f(5,3) + 3) f(6,3)=(f(5,3)+3) % 6 = 0 6 = 0 6=0
第7轮: f ( 7 , 3 ) = ( f ( 6 , 3 ) + 3 ) f(7,3) = (f(6,3) + 3) f(7,3)=(f(6,3)+3) % 7 = 3 7 = 3 7=3
这样我们就可以得到在数组n中,数字4的下标为3了。
以此继续递推:
当数组中元素为N时,我们删除第m个数字,最终剩下来的数字下标的求解方式可以写成通用公式:
i n d e x = f ( N , m ) = ( f ( N − 1 , m ) + m ) index = f(N,m) = (f(N-1,m) + m) index=f(N,m)=(f(N1,m)+m) % N N N


问题2:为什么以这种方式进行排序?需要注意什么?

由题意,数组中的元素按照顺序排成了一个圆,也就是说这个数组中的元素是循环的,即数组中的末尾元素 的下一个元素是数组中的首元素。
注意点: 为什么通用公式中进行取模,也就是求余。因为数组的长度是有限的,不取模在代码实现中会导致内存溢出。以上题为例:
在这里插入图片描述
如上图,我们恢复逆向推导中的第六轮到第七轮的过程:
**step1:**逆向推导的第7轮,也就是顺序推导的第一轮,在顺一轮中,我们剔除了3,因此第一步就是恢复数字3,也就是在数组的末尾在右移3位。也就是+m
**step2:**进行取模,可以看到
取模前数字3的下标为9,对下标进形取模即 9 9 9% 7 = 2 7 = 2 7=2
取模前数字2的下标为8,对下标进形取模即 8 8 8% 7 = 1 7 = 1 7=1
取模前数字1的下标为7,对下标进形取模即 7 7 7% 7 = 0 7 = 0 7=0
刚好对应取模后的元素下标位置。
这样就实现了元素间的循环。


实现代码如下:

int lastRemaining(int n, int m){
    int pos = 0;//对应前文中逆推的第一轮中最终剩下来的数字的位置:即f(1,3) = 0;
    for(int i = 2; i <= n; i++)//从逆推中的第二轮开始遍历,即f(2,3),f(3,3),...
    {
        pos = (pos+ m) % i;//递推公式
    }
    return pos;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徽州SLAM李

如果觉得不错,打赏一下哦,嘻

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值