【记录】【C++】【环形单向链表】【约瑟夫难题】【数据结构实验】(三)

一、实验作业相关要求

实验三 循环链表

1、实验目的与要求

1)熟悉循环链表的类型定义和基本操作;
2)灵活应用循环链表解决具体应用问题。

2、实验内容

有n个小孩围成一圈,给他们从1开始依次编号,从编号为1的小孩开始报数,数到第m个小孩出列,然后从出列的下一个小孩开始重新报数,数到第m个小孩又出列,……如此反复直到所有的小孩全部出列为止,求整个出列序列,例如:当n=6,m=5 时的出列序列是5,4,6,2,3,1 .
3、实验结果

1)请将调试通过的主要源代码、输出结果粘贴在下面

2)简述算法步骤,格式如下:
S1:
S2:
S3:

3)请分析算法的时间复杂度。

二、源代码

引用include部分
#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;
定义结点类
//定义结点类 
class LinkNode{
	public:
		int val;
		LinkNode* next;
		LinkNode() :val(0), next(NULL) {}
		LinkNode(int x) : val(x), next(NULL) {}
		LinkNode(int x, LinkNode *next) : val(x), next(next) {}
};
定义环形链表类
//定义环形链表类 
class CircleList: public LinkNode{
public:
	CircleList();
	~CircleList();
	bool makeEmpty();
	int getSize();
	void outAllelems();
	bool Insert(int pos,int val);
	bool Insert(int val);
	bool isEmpty();
	int outOneelem(int pos);
	int getPos(int val);
	bool remove(int pos);
	LinkNode*& getHeadnode();
	LinkNode*& getTailnode();
private:
	LinkNode* head;
	LinkNode* tail;
};
构造与析构
//初始化 
CircleList::CircleList(){
	head=new LinkNode();
	tail=head;
	tail->next=head;
}
//析构函数 
CircleList::~CircleList(){
	makeEmpty();
	delete head;
}
置为空表
//置为空表 
bool CircleList::makeEmpty(){
	if(tail==head){
		return true;
	}else{
		LinkNode* cur=head->next;
		while(cur){
			if(cur==tail){
				head->next=head;
				tail=head;
				delete cur;
				break;
			}
			cur=cur->next;
			delete head->next;
			head->next=cur;
		}
		return true;
	}
}
获取链表大小
//获取链表大小 
int CircleList::getSize(){
	if(tail==head){
		return 0;
	}
	LinkNode* cur=head->next;
	int output=0;
	while(cur){
		output++;
		if(cur==tail){
			break;
		}
		cur=cur->next;
	}
	return output;
}
输出所有元素
//输出所有元素 
void CircleList::outAllelems(){
	if(head==tail){
		return;
	}
	LinkNode* cur=head->next;
	while(cur){
		cout<<cur->val<<" ";
		if(cur==tail){
			break;
		}
		cur=cur->next;
	}
	cout<<endl;
}
在特定位置插入元素
//在特定位置插入元素 
bool CircleList::Insert(int pos,int val){
	if(pos<=0){
		cerr<<"Position Error!!!"<<endl;
		exit(-1);
	}else{
		if(this->isEmpty()){
			if(pos==1){
				this->Insert(val);
				return true;
			}else{
				cerr<<"Position Error!!!"<<endl;
				exit(-1);
			}
		}else{
			LinkNode* cur=head;
			for(int i=0;!bool(i&&cur==head);i++,cur=cur->next){
				if(pos==i+1){
					if(cur==tail){
						LinkNode* newnode=new LinkNode(val);
						newnode->next=head;
						tail->next=newnode;
						tail=newnode;
						return true;
					}else{
						LinkNode* newnode=new LinkNode(val);
						newnode->next=cur->next;
						cur->next=newnode;
						return true;
					}
				}
			}
			cerr<<"beyound range!!"<<endl;
			return false;
		}
	}
}
后插一个元素 和 判空
//后插一个元素 
bool CircleList::Insert(int val){
	LinkNode* newnode=new LinkNode(val);
	newnode->next=head;
	tail->next=newnode;
	tail=newnode;
	return true;
}
//判断是否为空 
bool CircleList::isEmpty(){
	return head==tail?true:false;
}
获取特定位置的元素和获取特定元素的第一个位置
//获取特定位置的元素 
int CircleList::outOneelem(int pos){
	if(pos<=0||this->isEmpty()){
		cerr<<"error"<<endl;
		exit(-1);
	}
	LinkNode* cur=head->next;
	for(int i=1;cur!=head;i++,cur=cur->next){
		if(i==pos){
			return cur->val;
		}
	}
	cerr<<"position error"<<endl;
	exit(-1);
}
//获取特定元素的第一个位置 
int CircleList::getPos(int val){
	if(this->isEmpty()){
		cerr<<"error"<<endl;
		exit(-1);
	}
	LinkNode* cur=head->next;
	for(int i=1;cur!=head;i++,cur=cur->next){
		if(val==cur->val){
			return i;
		}
	}
	cerr<<"position error"<<endl;
	exit(-1);
}
删除特定位置的结点
//删除特定位置的结点 
bool CircleList::remove(int pos){
	if(pos<=0){
		cerr<<"position error"<<endl;
		exit(-1);
	}else{
		if(this->isEmpty()){
			cerr<<"position error!!"<<endl;
			exit(-1);
		}else{
			LinkNode* cur=head;
			for(int i=0;cur!=tail;i++,cur=cur->next){
				if(pos==i+1){
					if(cur->next==tail){
						LinkNode* del=tail;
						cur->next=head;
						tail=cur;
						delete del;
						return true;
					}else{
						LinkNode* del=cur->next;
						cur->next=cur->next->next;
						delete del;
						return true;
					}
				}
			}
			cerr<<"beyound range!!!"<<endl;
			return false;
		}
	}
}
获取头尾指针的引用
//获取头结点的引用 
LinkNode*& CircleList::getHeadnode(){
	return head;
}
//获取尾结点的引用 
LinkNode*& CircleList::getTailnode(){
	return tail;
}
定义方法类(仿照力扣)
//定义方法类(仿照力扣) 
class Solution{
	public:
		//环形链表出列函数 
		vector<int>  separationOrderfromCircleList(CircleList& cl, int num, int skipTimes){
			vector<int>  list;
			if(cl.getHeadnode()==cl.getTailnode()){
				return list;
			}
			if(skipTimes>=num){
				for(int ptr=skipTimes%num;bool(num);num--,ptr=(ptr-1+skipTimes)%max(num,1)){ //
					list.push_back(cl.outOneelem((ptr=(ptr==0?num:ptr))));
					cl.remove(ptr);
				}
				return list;
			}else{
				LinkNode* cur=cl.getHeadnode();
				for(int i=1;i<=num;i++){
					for(int j=1;j<=skipTimes-1;j++,cur=(cur->next==cl.getTailnode()?cl.getHeadnode():cur->next));
					list.push_back(cur->next->val);
					LinkNode* del=cur->next;
					cur->next=del->next;
					if(cl.getTailnode()==del){
						cl.getTailnode()=cur;
						cur=cl.getHeadnode();
					}
					delete del;
				}
				return list;
			}
			
		}
		
};
主函数部分
int main(){
	int n,m;
	cin>>n>>m;
	CircleList cl;
	//插入链表中n个编号 
	for(int i=1;i<=n;i++){
		cl.Insert(i);
	}
	
	vector<int> output=Solution().separationOrderfromCircleList(cl,n,m);
	
	for(int i=0;i<output.size();i++){
		cout<<output[i]<<" ";
	}
	cout<<endl;
	return 0;
}

执行效果
当输入n=6,m=5时,

在这里插入图片描述

当输入n=8,m=3时,
在这里插入图片描述

当输入n=6,m=9时,
在这里插入图片描述

三、作业提交的文档内容

1)请将调试通过的主要源代码粘贴在下面

如上

2)简述算法步骤:

S1:定义链表复合类并且实现其基本操作如插入、删除等

S2:定义Solution工具类,在其中完成解约瑟夫难题的基本过程

S3:主函数创建一个环形链表,并调用工具类函数,输出答案

3)请分析算法的时间复杂度。

简而言之,对于一个环形单向链表
前插、后插时间复杂度均为O(1)
插入、删除特定位置元素的时间复杂度为O(n)
其余需遍历链表操作的时间复杂度均为O(n)
对于解约瑟夫难题的部分
m(每m个人出列)<n(在n个人中) 时,
此时可采用双层遍历,第一层n次,第二层m次,意思就是对于每次查找一个人出列都循环m次,供需使n(或n-1)个人出列,这是典型的暴力解法,时间复杂度为 O(m*n)
而当 m>=n 时,
采用上述做法显然会重复遍历元素,开始绕圈,所以可以加一点,用m整除n来简化第二层循环,使第二层循环最多n次即可,时间复杂度为 O(n^2)

vector<int>  separationOrderfromCircleList(CircleList& cl, int num, int skipTimes){
			vector<int>  list;
			if(cl.getHeadnode()==cl.getTailnode()){
				return list;
			}
			if(skipTimes>=num){
				for(int ptr=skipTimes%num;bool(num);num--,ptr=(ptr-1+skipTimes)%max(num,1)){ //
					list.push_back(cl.outOneelem((ptr=(ptr==0?num:ptr))));
					cl.remove(ptr);
				}
				return list;
			}else{
				LinkNode* cur=cl.getHeadnode();
				for(int i=1;i<=num;i++){
					for(int j=1;j<=skipTimes-1;j++,cur=(cur->next==cl.getTailnode()?cl.getHeadnode():cur->next));
					list.push_back(cur->next->val);
					LinkNode* del=cur->next;
					cur->next=del->next;
					if(cl.getTailnode()==del){
						cl.getTailnode()=cur;
						cur=cl.getHeadnode();
					}
					delete del;
				}
				return list;
			}
			
		}

综上,解约瑟夫难题的时间复杂度为 min(O(n^2),O(m*n))

补充:看到有关于约瑟夫问题的 O(n) 时间复杂度的进阶算法
链表问题—环形单链表的约瑟夫问题_冰殇的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值