剑指Offer之 - 圆圈中最后剩下的数字

来源:互联网 发布:商城订单数据库设计 编辑:程序博客网 时间:2024/05/23 21:13

题目:

0,1,...,n-1这n个数排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删词第3个数字,则删除的前四个数字一次是2、0、4、1,因此最后剩下的数字是3.

思路:

方法1:用环形链表模拟圆圈,链表可以用c++的STL库中的list来表示,删除链表其中一个结点之后,链表还是连续的。

方法2:定义f(n,m),表示在n个数字0,1,...,n-1中每次删除第m个数字最后剩下的数字。

先给出关系公式:

   

可以这样理解:从序列f(n):0~n-1中删除第m个数后,剩下为序列m,m+1,..,n-1,0,1,...,m-2,即序列f(n-1)+m.因此f(n,m) =[f(n-1,m)+m]%n,是采用的映射关系来递推的。

即要得到n个数字的序列中最后剩下的数字,只需要得到n-1个数字的序列中最后剩下的数字,并以此类推。当n=1时,即序列中开始只有一个数字0,那么最后剩下的数字就是0。

代码:

#include <iostream>#include <list>using namespace std;//题目:0,1,……,n-1这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字。//求出这个圆圈里剩下的最后一个数字。//例如,0、1、2、3、4这5个数字组成一个圆圈,从0开始每次删除第3个数字(删完之后到其后的数字),则删除的前4个数字依次是2、0、4、1//因此最后剩下的数字是3.//本题就是有名的约瑟夫环问题。两种方法:1、环形链表模拟 2、找出规律,直接计算//1、用环形链表模拟圆圈int LastRemaining(unsigned n , unsigned m){if(n < 1 || m < 1)return -1;unsigned int i = 0;list<int> numbers;for(i = 0 ; i < n ; i++){numbers.push_back(i);}list<int>::iterator current = numbers.begin();while(numbers.size() > 1){//找打第m个位置for(int i = 1 ; i < m ; i++){current++;if(current == numbers.end())current = numbers.begin();}//cout<<"删除的数是:"<< *current<<endl;current = numbers.erase(current);//直接删除current,返回current的后一个迭代器(有可能是end)if(current == numbers.end()){current = numbers.begin();}}return *current;}int LastRemaining2(unsigned n , unsigned m){if(n < 1 || m < 1)return -1;int last = 0;for(int i = 2 ; i <= n ; i++){last = (last + m) % i;//i就是上面递推公式的n,i由2一直增长到n}return last;}int main(){cout<<"begin"<<endl;int number2 = LastRemaining2(1000000 , 3);cout<<number2<<endl;int number = LastRemaining(1000000 , 3);cout<<number<<endl;}



0 0
原创粉丝点击