约瑟夫环问题-链表-删头添尾法

一、问题描述

       有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模板就不行了,其成员函数没有现成的。

这也是我们为什么要学习基本原理的原因,没有的时候,你有能力创造。

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页