剑指offer:圆圈中最后剩下的数字(约瑟夫环)

2024-08-24 00:38

本文主要是介绍剑指offer:圆圈中最后剩下的数字(约瑟夫环),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目描述:

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

思路:

方法1:用环形链表模拟圆圈,每次到第m个节点删除对应节点,知道最后剩下一个数字。比较费时,每删除一个元素需要m步运算,一共n个元素,所以时间复杂度O(mn),空间复杂度为O(n)。

实现思路a:用模板库的std:list来模拟环形链表,因为链表可以方便的删除节点,但是因为不是环,当迭代器走到尾部时也要手动将其置为头部。

注意list的使用:

1)定义:list<int> num;

2)迭代器的定义: list<int>::iterator curr=num.begin();

3)vector容器的迭代器可以加1减1,也可以++,--,但是list容器的迭代器不能加1减1,只能++,--。原因是list容器不支持像vector容器那样的快速随机访问,只能依次访问每个元素,所以不支持加减等数术运算。

4)用迭代器访问元素需要解引用:*iter。

5)用erase删除元素。

对于关联容器(如map,set,multimap,multiset),删除当前的iterator,仅仅会使当前的iterator失效,只要在erase时,递增当前的iterator即可。

对于序列式容器(如vector,deque,list等),删除当前的iterator会使后面所有元素的iterator都失效。这是因为vector,deque使用了连续分配的内存,删除一个元素导致后面所有的元素会向前移动一个位置。没有失效啊???

不过erase方法可以返回下一个有效的iterator。所以下面的代码还有一种更简洁的写法可以不用next迭代器。

 

实现思路b:用数组实现,因为数组访问元素很方便,而且下标就是对应的数字。首先数组删除节点不方便,所以直接将要删除的节点设为-1。其次当遍历到i==n时,要重新将i设为0,从头开始。先申请一个长度为n的动态数组,然后把0~n的值放进去,用count记录每一轮的计数,每次count=m时,将此时i处数字设为-1,之后每次遍历到-1的点直接跳过,用sum记录数组中不为-1的数,当sum==0时代表所有数字都已删除,此时的下标对应最后一个被删除的位置,也是最后剩下的数字。

方法2:分析每次删除的数字规律,时间复杂度O(n),空间复杂度为O(1)。

设f(n,m)表示从长度为n的序列中删除第m个数字最后剩下的数字,则有递推公式:

f(n,m)=\left\{\begin{matrix} &0, n=1 \\ &[f(n-1,m)+m]\%n, n>1 \end{matrix}

分析过程:定义一个函数f(n,m)表示从长度为n的序列中每次删除第m个数字最后剩下的数字,删除的第一个元素是k=(m-1)\%n,删除这个元素之后的序列是L1:0,1,2,...,k-1,k+1,...,n-2,并且下一次从k+1开始计数,重排序列为P2:k+1,...,n-2,0,1,2,...,k-1。用另一个函数g(n-1,m)表示从重排后长度为n-1的序列P2中每次删除第m个数字最后剩下的数字(下标不一样了,所以函数关系变了),由于都是接着第一步之后进行完剩下的删除,所以两个操作最后剩下的数字都一样,即f(n,m)=g(n-1,m)。下面建立一个P2到顺序下标序列P3:0,1,2,...,n-2的映射,这个映射关系是P:L2\rightarrow L3: x\rightarrow y=(x-k-1)\%n,由于映射后的序列是从0开始排序,也是从0开始删除,只是序列长度为n-1,所以最后剩下的数字可以用f(n-1,m)表示。可知有g(n-1,m)=P^{-1}(f(n-1,m))=(f(n-1,m)+k+1)\%n,把k代入可得到f(n,m)=(f(n-1,m)+m)\%n

参考代码:

在线测试

https://www.nowcoder.com/practice/f78a359491e64a50bce2d89cff857eb6?tpId=13&tqId=11199&tPage=3&rp=3&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

AC代码

方法1思路a:list

class Solution {
public:int LastRemaining_Solution(int n, int m){if(n<=0||m<=0)return -1;list<int> num;for(int i=0;i<n;i++){num.push_back(i);}list<int>::iterator curr=num.begin();//直到最后只剩下一个数字while(num.size()>1){//j从1开始for(int j=1;j<m;j++){curr++;if(curr==num.end())curr=num.begin();}//结束循环时curr指向要删除的位置/*写法1curr++;list<int>::iterator next=curr;//保存下一个起始位置if(next==num.end())next=num.begin();curr--;num.erase(curr);//删除当前位置curr=next;//从下一个位置开始重新计数*///写法2,更简洁curr=num.erase(curr);//erase删除当前的返回下一个迭代器if(curr==num.end())curr=num.begin();}return *(curr);//迭代器解引用}
};

方法1思路b:数组

class Solution {
public:int LastRemaining_Solution(int n, int m){if(n<=0||m<=0)return -1;int *num=new int[n];for(int i=0;i<n;i++){num[i]=i;}int count=0;//计数变量count初始化int sum=n;//剩下的节点数int index=-1;while(sum){index++;//如果还有节点剩下,index往后走if(index==n)//如果往后走到尾了,再回到头部index=0;if(num[index]==-1)//如果往后走了发现节点已删除,直接跳过,继续index++,不对count和sum以及数组中元素做任何操作continue;count++;//如果节点有效,count加1if(count==m)//已经是第m个点{num[index]=-1;//删除count=0;//count清零sum--;//剩下的节点少1}}return index;}
};

方法2:

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

 

这篇关于剑指offer:圆圈中最后剩下的数字(约瑟夫环)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1100955

相关文章

从去中心化到智能化:Web3如何与AI共同塑造数字生态

在数字时代的演进中,Web3和人工智能(AI)正成为塑造未来互联网的两大核心力量。Web3的去中心化理念与AI的智能化技术,正相互交织,共同推动数字生态的变革。本文将探讨Web3与AI的融合如何改变数字世界,并展望这一新兴组合如何重塑我们的在线体验。 Web3的去中心化愿景 Web3代表了互联网的第三代发展,它基于去中心化的区块链技术,旨在创建一个开放、透明且用户主导的数字生态。不同于传统

usaco 1.2 Name That Number(数字字母转化)

巧妙的利用code[b[0]-'A'] 将字符ABC...Z转换为数字 需要注意的是重新开一个数组 c [ ] 存储字符串 应人为的在末尾附上 ‘ \ 0 ’ 详见代码: /*ID: who jayLANG: C++TASK: namenum*/#include<stdio.h>#include<string.h>int main(){FILE *fin = fopen (

poj3750约瑟夫环,循环队列

Description 有N个小孩围成一圈,给他们从1开始依次编号,现指定从第W个开始报数,报到第S个时,该小孩出列,然后从下一个小孩开始报数,仍是报到S个出列,如此重复下去,直到所有的小孩都出列(总人数不足S个时将循环报数),求小孩出列的顺序。 Input 第一行输入小孩的人数N(N<=64) 接下来每行输入一个小孩的名字(人名不超过15个字符) 最后一行输入W,S (W < N),用

AIGC6: 走进腾讯数字盛会

图中是一个程序员,去参加一个技术盛会。AI大潮下,五颜六色,各种不确定。 背景 AI对各行各业的冲击越来越大,身处职场的我也能清晰的感受到。 我所在的行业为全球客服外包行业。 业务模式为: 为国际跨境公司提供不同地区不同语言的客服外包解决方案,除了人力,还有软件系统。 软件系统主要是提供了客服跟客人的渠道沟通和工单管理,内部管理跟甲方的合同对接,绩效评估,BI数据透视。 客服跟客人

PHP约瑟夫环问题

#循环 function circle($arr,$idx,$k){for($i=0;$i<$idx;$i++){$tmp = array_shift($arr);array_push($arr,$tmp);}$j = 1;while(count($arr) > 0){$tmp = array_shift($arr);if($j++%$k == 0){echo $tmp."\n";}else{a

NC 把数字翻译成字符串

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 描述 有一种将字母编码成数字的方式:‘a’->1, ‘b->2’, … , ‘z->26’。 现在给一串数字,返回有多少种可能的译码结果 import java.u

34465A-61/2 数字万用表(六位半)

34465A-61/2 数字万用表(六位半) 文章目录 34465A-61/2 数字万用表(六位半)前言一、测DC/AC电压二、测DC/AC电流四、测电阻五、测电容六、测二极管七、保存截图流程 前言 1、6位半数字万用表通常具有200,000个计数器,可以显示最大为199999的数值。相比普通数字万用表,6位半万用表具有更高的测量分辨率和更高的测量准确度,适用于精度比较高的测

超级 密码加密 解密 源码,支持表情,符号,数字,字母,加密

超级 密码加密 解密 源码,支持表情,符号,数字,字母,加密 可以将表情,动物,水果,表情,手势,猫语,兽语,狗语,爱语,符号,数字,字母,加密和解密 可以将文字、字母、数字、代码、标点符号等内容转换成新的文字形式,通过简单的文字以不同的排列顺序来表达不同的内容 源码截图: https://www.httple.net/152649.html

两个长数字相加

1.编程题目 题目:要实现两个百位长的数字直接相加 分析:因为数字太长所以无法直接相加,所以采用按位相加,然后组装的方式。(注意进位) 2.编程实现 package com.sino.daily.code_2019_6_29;import org.apache.commons.lang3.StringUtils;/*** create by 2019-06-29 19:03** @autho

关于字符串转化为数字的深度优化两种算法

最近在做项目,在实际操作中发现自己在VC环境下写的字符串转化为整型的函数还是太过理想化了,或者说只能在window平台下软件环境中运行,重新给大家发两种函数方法: 第一个,就是理想化的函数,在VC环境下充分利用指针的优越性,对字符串转化为整型(同时也回答了某位网友的答案吖),实验检验通过: #include <stdio.h> #include <string.h> int rayatoi(c