Day46:孩子们的游戏(圆圈中最后剩下的数)
剑指Offer_编程题——孩子们的游戏(圆圈中最后剩下的数)
题目描述:
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1) 如果没有小朋友,请返回-1
具体要求:
时间限制: C/C++ 1秒,其他语言2秒 空间限制: C/C++32M,其他语言64M
具体实现
背景介绍 这个问题是一个典型的约瑟夫问题,在介绍解题方法之前,首先给大家介绍约瑟夫问题。在维基百科中是这样介绍约瑟夫问题的。约瑟夫问题(有时也称为约瑟夫斯置换),是一个出现在计算机科学和数学中的问题。在计算机编程的算法中,类似问题又称为约瑟夫环。其问题是:
人们站在一个等待被处决的圈子里。 计数从圆圈中的指定点开始,并沿指定方向围绕圆圈进行。 在跳过指定数量的人之后,执行下一个人。 对剩下的人重复该过程,从下一个人开始,朝同一方向跳过相同数量的人,直到只剩下一个人,并被释放。
这就是典型的约瑟夫问题,根据这个问题,我们给出以下的解法: 思路一 上面介绍了约瑟夫的相关问题,接下来我们用约瑟夫思想来解决该题。具体的方法如下:首先定义n个人,报m次的解是f(n,m),n,m以及编号为0的小朋友确定了,最后的幸运儿也就确定了,第一次报数后,m-1的小朋友出列,接着从m开始报数,但是最后的解是一样的,令g(n-1,m)表示第二次抽人,解是等于f(n,m)的,这两种情况的排列方式不同,一个从0开始报,一个从m开始报,所以我们把第二种情况也排成从0开始报,就是每个人的下标都减少m即可,那么最后的幸运儿加上m,就得出在原来序列的下标了,解就是f(n-1,m)+m,构成了一个递归问题。但由于是一个循环的环,超出环从头排起,就对每一次环的长度进行取余。关系式为f(n,m) = (f(n-1,m) + m)%n。接下来我们分别用java和python将其实现: 1、我们首先用java将其实现:
import java.util.LinkedList;
public class Solution{
public int LastRemaining_Solution(int n, int m){
if(n <= 0 || m < 0)
return -1;
LinkedList<Integer> list = new LinkedList<>();
for(int i = 0; i < n; i++)
list.add(i);
int removeIndex = 0;
while(list.size()>1){
removeIndex = (removeIndex + m - 1) % list.size();
list.remove(removeIndex);
}
return list.get(0);
}
}
代码效果图如图所示:
2、我们用python将其实现:
class Solution:
def LastRemaining_Solution(self, n, m):
if n < 1 or m < 1:
return -1
if n == 1:
return 0
Prevalue = 0
for i in range(2, n+1):
value = (Prevalue + m) % i
Prevalue = value
return value
代码效果图如图所示:
思路二 我们可以用拼接法来解决该问题。具体用java实现如下:
import java.util.LinkedList;
public class Solution{
public int LastRemaining_Solution(int n, int m) {
if(n <= 0 || m < 0)
return -1;
LinkedList<Integer> list = new LinkedList<>();
for(int i = 0; i < n; i++)
list.add(i);
int removeIndex = 0;
while(list.size() > 1){
for(int i = 0; i < m-1; i++){
removeIndex++;
if(removeIndex == list.size())
removeIndex = 0;
}
list.remove(removeIndex);
if(removeIndex == list.size())
removeIndex = 0;
}
return list.get(0);
}
}
代码效果图如图所示:
思路三: 根据本题题意可知:第一个要删除的是m-1,第二个要删除是2m-1,第三个要删除是3m-1,由于有n的限制即用求模来解决。具体我们用python将其实现:
class Solution:
def LastRemaining_Solution(self, n, m):
if not m or not n:
return -1
res = range(n)
i = 0
while len(res) > 1:
i = (m + i - 1) % len(res)
res.pop(i)
return res[0]
代码效果图如图所示:
这里我们需要西湖一的是:算法中i=(m+i-1)%len(res),其中为何要减一,是因为已经从res删除了一个数,res后面的数下标会在原来的基础上减少1,所以2m-1的下标变成了2m-2,从而整个算法就可以理解了。 算法四: 将0-n-1构建成一个环形链表,尾结点的next指向头结点。然后对链表进行循环操作,指针走m步删除当前结点,即将前一结点的next连接到后一节点,这样达到删除的效果,直到指针的next结点等于指针本身,这代表链表只剩下一个元素,退出循环返回该元素值即可:接下来我们用python将其实现:
class Node:
def __init__(self, val):
self.val = val
self.next = None
class Solution:
def LastRemaining_Solution(self, n, m):
if n == 0:
return -1
if n == 1:
return 0
head = Node(0)
cur = head
for i in range(1, n):
cur.next = Node(i)
cur = cur.next
cur.next = head
p = cur
while p.next != p:
count = 0
while count < m - 1:
p = p.next
count += 1
p.next = p.next.next
return p.val
代码效果图如图所示:
总结
本道题通过与上一道题类似,均是通过实际问题来考察我们对链表以及约瑟夫问题的理解,我们对本题给出了四种解题思路,均分别用java和python将其实现。首先给出的就是我们介绍的约瑟夫问题来解决的问题,其次是根据拼接的方式将其解决。最后就是通过循环链表来将其解决。因此,我们在做题的时候,应该多次尝试各种方法,扩展自己的思维,写出优质的代码。总之,我们要继续加油,争取早日找到工作,Good Luck!!!
参考文献
[1] 白马长枪儒雅将 [2] weixin_43160613 [3] 草木向阳 [4] Mr-Yu23
- R语言 使用BP神经网络进行银行客户信用评估
- 使用R语言挖掘QQ群聊天记录
- 解析滴滴算法大赛---GBDT进行数据预测
- 数据迁移中的数据库检查和建议(r2笔记71天)
- 决策树案例:基于python的商品购买能力预测系统
- 数据迁移前的准备和系统检查 (r2笔记70天)
- 数据处理的统计学习(scikit-learn教程)
- 机器学习实战,使用朴素贝叶斯来做情感分析
- Python NLTK 处理原始文本
- 通过闪回事务查看数据dml的情况 (r2笔记69天)
- 通过shell和sql结合查找性能sql(r2笔记68天)
- 淘宝的评论归纳是用什么方法做到的?
- Python的机器学习实战:AadBoost
- 通过shell检查分区表中是否含有默认分区(r2笔记87天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- LDAP 中 DN CN DC OU
- Spring Boot入门系列(十九)集成mybatis
- 当我们做后仿时我们究竟在仿些什么(四)
- Scala学习一
- kafka学习二 -发送消息
- 【ACL2020】使用问题图生成解决multi-hop复杂KBQA
- R语言实现DNA结构预测
- Vue 基础入门
- Chrome DevTools 一些隐藏技巧
- Leetcode 1160. 拼写单词 (Hash)
- CodeForces 23B (图论 思维)
- JDBC 进阶操作
- Leetcode 面试题 01.06. 字符串压缩 (字符串模拟)
- Centos 7 安装 Git
- 受限平均生存时间(Restricted mean survival time)简析及R语言实现