题目:n个数字(0,1,…,n-1)形成一个圆圈,从数字0开始,每次从这个圆圈中删除第m个数字(第一个为当前数字本身,第二个为当前数字的下一个数字)。当一个数字删除后,从被删除数字的下一个继续删除第m个数字。求出在这个圆圈中剩下的最后一个数字。
1、大部分面试官只要求应聘者基于环形链表的方法解决这个问题。
/*************************************************************************
> Created Time: Tue 03 May 2016 01:37:26 PM PKT
************************************************************************/
#include<iostream>
using namespace std;
#include <list>
int LastInCircle(int n,int m)
{
if(n<1 || m<1){
return -1;
}
list<int> lt;
for(int i=0;i<n;i++){
lt.push_back(i);
}
list<int>::iterator ite=lt.begin();
while(lt.size()>1){
for(int i=1;i<m;i++){ //从1开始,很重要,避免错误
ite++;
if(ite==lt.end()){
ite=lt.begin();
}
}
ite=lt.erase(ite);
if(ite==lt.end()){
ite=lt.begin();
}
}
return *ite;
}
//test
int main()
{
int result=LastInCircle(5,3);
cout<<result<<endl;
return 0;
}
有n个结点的环形列表来模拟这个删除的过程,因此内存开销为O(n)。而且这种方法每删除一个数字需要m步运算,总共有n个数字,因此总的时间复杂度是O(mn)。当m和n都很大的时候,这种方法是很慢的。
2、数学方法
根据数学规律可推导出如下递归公式,推导过程复杂,但实现容易,时间复杂度为O(n),空间复杂度为O(1)。
f(n,m)={
0 n=1
[f(n-1,m)+m]%n n>1
//方法2
int LastInCircle2(int n,int m)
{
if(n<1 || m<1){
return -1;
}
int last=0; //n=1
for(int i=2;i<=n;i++){
last=(last+m)%i;
}
return last;
}