一、问题描述
有15个人围成一圈,顺序从1到15编号。从第一个人开始报数,凡报到n的人退出圈子。用C语言写出程序,输入n(n>1)的值,输出最后留在圈子里的人的编号。
二、算法描述
void main( )
{
构建链表 L
把1-15这15个数逐个插入到 L 的尾部
输入n
While(链表 L 中元素多于一个)
{
for(i=1;i<n;i++)
{
取 L第一个元素 放到L的尾部;
删除L第一个元素
}
输出L第一个元素
删除 L第一个元素
}
输出L第一个元素,就是剩余的那一个
}
三、实现
#include<list>
#include<iostream>
using namespace std;
void print(list<int> &L)
{
if(L.size()==0)
{
cout<<"No data\n";
return;
}
list<int>::iterator it;
for(it=L.begin();it!=L.end();it++)
{
cout<<*it<<" ";
}
cout<<endl;
}
int main( )
{
list<int> L;
int i,n;
for(i=1;i<16;i++)
{
L.push_back(i);
}
//cin>>n;
n=3;
while(L.size()>1)
{
for(i=1;i<n;i++)
{
L.push_back(L.front());
L.erase(L.begin());
}
cout<<L.front()<<" ";
L.erase(L.begin());
}
cout<<endl;
cout<<"最后剩余的是:";
cout<<L.front()<<" "<<endl;
return 0;
}
四、效率分析
这是一个双重循环,假设开始时有m个人
外层第 1 次循环时,插入尾部n-1次,删除头部n次。
外层第 2 次循环时,插入尾部n-1次,删除头部n次。
...
外层第 m-1 次循环时,插入尾部n-1次,删除头部n次。
综上,需要插入尾部操作:(m-1)*(n-1)次;需要删除操作:(m-1)*n次
所以,该算法的时间复杂度为:O(m*n)
若命题人故意刁难,给一个比较大的n,每次都需要数n次,有可能在不停地转圈圈
因此,内部循环次数 n 是可以优化成x的。
if(n%L.size()==0)//若n是表长的倍数,就是要删除表尾
x=L.size();
else//否则,就是求余
x=n%L.size();
#include<list>
#include<iostream>
using namespace std;
void print(list<int> &L)
{
if(L.size()==0)
{
cout<<"No data\n";
return;
}
list<int>::iterator it;
for(it=L.begin();it!=L.end();it++)
{
cout<<*it<<" ";
}
cout<<endl;
}
int main( )
{
list<int> L;
int i,n;
for(i=1;i<16;i++)
{
L.push_back(i);
}
//cin>>n;
n=3;
while(L.size()>1)
{
int x;
if(n%L.size()==0)
x=L.size();
else
x=n%L.size();
for(i=1;i<x;i++)
{
L.push_back(L.front());
L.erase(L.begin());
}
cout<<L.front()<<" ";
L.erase(L.begin());
}
cout<<endl;
cout<<"最后剩余的是:";
cout<<L.front()<<" "<<endl;
return 0;
}
还可以优化:即,把前n-1个数据构成的链表整体摘下来,插入到表尾。
这时候,用list模板就不行了,其成员函数没有现成的。
这也是我们为什么要学习基本原理的原因,没有的时候,你有能力创造。