题目要求:
0,1,...,n-1这n个数排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删词第3个数字,则删除的前四个数字一次是2、0、4、1,因此最后剩下的数字是3.
参考资料:剑指offer第45题
题目分析:
方法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,是采用的映射关系来递推的。
代码实现:
#include <iostream>
#include <list>
using namespace std;
int LastRemaining(unsigned int n,unsigned int m);
int LastRemaining2(unsigned int n,unsigned int m);
int main(void)
{
//cout << LastRemaining(5,3) << endl;
cout << LastRemaining2(5,3) << endl;
return 0;
}
//方法1
int LastRemaining(unsigned int n,unsigned int m)
{
if(n<1 || m<1)
return -1;
unsigned int i = 0;
list<int> nums;
for(i= 0;i<n;i++)
nums.push_back(i);
list<int>::iterator cur = nums.begin();
while(nums.size()>1)
{
for(int i = 1;i<m;i++)
{
cur++;
if(cur==nums.end())
cur = nums.begin();
}
list<int>::iterator next = ++cur;
if(next == nums.end())
next = nums.begin();
--cur;
nums.erase(cur);
cur = next;
}
return *cur;
}
//方法2
int LastRemaining2(unsigned int n,unsigned int m)
{
if(n<1 || m<1)
return -1;
int last = 0;
for(int i = 2;i<=n;i++)
last = (last+m)%i;
return last;
}