用循环链表求解约瑟夫环问题(c++实现)

前言

初学数据结构,关于循环链表,目前还在表层,特以约瑟夫环为例学习循环链表,全文仅供参考,如有不足的地方还望多多指教。


提示:以下是本篇文章正文内容,下面案例可供参考

一、约瑟夫环

据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

二、关于循环链表

源自个人理解

1.一种线性表,这里讨论的是单循环链表(个人比较适应带头结点的单链表,一下进行的链表都是带头结点的链表);

2.单循环链表,其实就是将单链表的尾结点的next指针指向头结点;

3.单循环链表实现了结点不能访问直接前驱的不足,通过循环遍历,以访问到当前结点的前驱;

4.单循环链表的遍历需要注意在尾部设置暂停条件;

5.单循环链表在插入新结点时,使用尾插法时需要注意尾结点的next指针要指向头结点;

6.单循环链表在删除元素时,也要注意尾部结点删除时要注意next指针的指向;

7.综上,我在使用单循环链表时,也采用了建立尾指针指向尾结点,则有last->next = first;

1.基本前提

这里在创建结点时和定义类时,由于是解决约瑟夫环的问题,故可以不用模板,我这里仅供参考。

​
template<class DataType>
struct Node{
	DataType data;
	Node * next;
};//创建结点

template<class DataType>
class CirculateList{
public:
	CirculateList();//无参构造
	~CirculateList();//析构
	void Insert(DataType elem);//插入
	void Delete_elem(Node<DataType>* elem);//删除
	void Display();//打印
	int GetLength(){ return length; }//获取长度
	void CirculateDelete();//循环删除(关键)
private:
	Node<DataType> * space;//first
	Node<DataType> * last;//尾指针
	int length;//链表长度
};

​

 2.无参构造函数

template<class DataType>
CirculateList<DataType>::CirculateList(){
	length = 0;//设置长度为0
	space = new Node<DataType>;//创建头结点
	space->next = space;//初始化next指针指向
	last = space;//初始化尾指针
}

3.析构函数

​
template<class DataType>
CirculateList<DataType>::~CirculateList(){
	if (space == nullptr){
		cout << "链表为空" << endl;
		exit(0);
	}

	Node<DataType> * p = space->next; //指向链表的第一个有数据的结点
	while (last != p)//该循环只删除了length-1个结点
    {
		Node<DataType> * q = p;
		p = p->next;
		delete q;//这里不能与上一句代码交换位置,不让会造成程序无法正常清除数据
                    //因为,如果先删除q,则会造成p->next无意义
	}
	delete last;//删除尾结点
	delete space;//清除头结点
}

​

4.插入操作

​
template<class DataType>
void CirculateList<DataType>::Insert(DataType elem){
	if (space == nullptr){
		cout << "链表已被删除" << endl;
		return;
	}
	Node<DataType> * temp = new Node<DataType>;
	temp->data = elem;
	//头插法
	/*temp->next = p->next;
	p->next = temp;*/

	//尾插法(个人喜好尾插法)
	temp->next = last->next;//让新结点的next指针指向头结点
	last->next = temp;//连接新结点,使得新结点有直接前驱
	last = temp;//last指向尾结点

	length++;//元素个数加1
}

​

5.删除指定结点

//这里指定结点应该都是为链表的结点
template<class DataType>
void CirculateList<DataType>::Delete_elem(Node<DataType>* elem){
	Node<DataType> * p = space;
	while (last != p->next){
		if (p->next == elem){
			Node<DataType> * q = p->next;
			p->next = q->next;//跳跃指向
			delete q;
			length--;
			return;
		}
		p = p->next;
	}
    //针对尾结点的删除
	if (last == p->next){
		p->next = space;//注意循环条件
		delete last;
		last = p;//重新确定为指针
		length--;
	}

}

6.循环删除(关键步骤)

template<class DataType>
void CirculateList<DataType>::CirculateDelete(){
	int count = 1;//控制循环报数
	Node<DataType>* p = space->next;
	/*
	//测试代码,测试是否可以循环链表
	初始条件:count = 100;
	while (count){
		if (last->next == p){
			p = p->next;
		}
		cout << p->data << " ";
		
		p = p->next;
		count--;
	}
	cout << endl;
    */

	while (length > 2)//保留最后两位,链表最终剩下两个结点
	{
	
		if (p == space)//如果p指针指向头结点,则移动p指针指向下一个结点的位置
		{
			p = p->next;
		}
		if (count == 3)//当报数报到3时就删除对应结点
		{
			Node<DataType>*q = p;
			p = p->next;
			//cout << "删除元素:" << q->data << endl;
			Delete_elem(q);
			//cout << "链表:";
			//Display();
			count = 1;
		}
		if (p == space)//针对删除last指向的结点后,指针p指向头结点,故需要将p移到下一个结点的            
                          //位置
		{
			p = p->next;
		}
		else{
			p = p->next;
			count++;
		}

	}
}

7.打印链表

template<class DataType>
void CirculateList<DataType>::Display(){
	if (length == 0){
		cout << "List is empty" << endl;
		return;
	}
	Node<DataType> * p = space->next;
	while (last->next != p){
		cout << p->data << " ";
		p = p->next;
	}
	cout << endl;
}

三.完整代码展示

#include <iostream>
using namespace std;

template<class DataType>
struct Node{
	DataType data;
	Node * next;
};//创建结点

template<class DataType>
class CirculateList{
public:
	CirculateList();//无参构造
	~CirculateList();//析构
	void Insert(DataType elem);//插入
	void Delete(int i);//删除
	void Delete_elem(Node<DataType>* elem);//删除
	void Display();//打印
	int GetLength(){ return length; }//获取长度
	void CirculateDelete();//循环删除
private:
	Node<DataType> * space;
	Node<DataType> * last;

	int length;
};

//无参构造
template<class DataType>
CirculateList<DataType>::CirculateList(){
	length = 0;
	space = new Node<DataType>;
	space->next = space;
	last = space;
}

//析构
template<class DataType>
CirculateList<DataType>::~CirculateList(){
	if (space == nullptr){
		cout << "链表为空" << endl;
		exit(0);
	}
	/*Node<DataType> * p = space;
	for (int i = 0; i < length + 1; ++i){
		space = space->next;
		delete p;
		p = space;
	}*/

	Node<DataType> * p = space->next;
	while (last != p){
		Node<DataType> * q = p;
		p = p->next;
		delete q;
	}
	delete last;
	delete space;
}

//插入
template<class DataType>
void CirculateList<DataType>::Insert(DataType elem){
	if (space == nullptr){
		cout << "链表已被删除" << endl;
		return;
	}
	Node<DataType> * temp = new Node<DataType>;
	temp->data = elem;
	//头插法
	/*temp->next = p->next;
	p->next = temp;*/

	//尾插法
	temp->next = last->next;
	last->next = temp;
	last = temp;

	length++;
}

//按位删除
template<class DataType>
void CirculateList<DataType>::Delete(int i){
	if (space == nullptr){
		cout << "List is empty" << endl;
		return;
	}
	if (i<1 || i>length){
		cout << "位置错误" << endl;
		return;
	}
	int count = 0;
	Node<DataType> * p = space;
	while (count < i-1){
		count++;
		p = p->next;
	}
	Node<DataType>* temp = p->next;
	p->next = temp->next;
	length--;
}

//删除指定的结点
template<class DataType>
void CirculateList<DataType>::Delete_elem(Node<DataType>* elem){
	Node<DataType> * p = space;
	while (last != p->next){
		if (p->next == elem){
			Node<DataType> * q = p->next;
			p->next = q->next;
			delete q;
			length--;
			return;
		}
		p = p->next;
	}
	if (last == p->next){
		p->next = space;
		delete last;
		last = p;
		length--;
	}

}


//打印链表
template<class DataType>
void CirculateList<DataType>::Display(){
	if (length == 0){
		cout << "List is empty" << endl;
		return;
	}
	Node<DataType> * p = space->next;
	while (last->next != p){
		cout << p->data << " ";
		p = p->next;
	}
	cout << endl;
}

//循环删除
template<class DataType>
void CirculateList<DataType>::CirculateDelete(){
	int count = 1;
	Node<DataType>* p = space->next;
	/*
	//测试代码,测试是否可以循环链表
	初始条件:count = 100;
	while (count){
		if (last->next == p){
			p = p->next;
		}
		cout << p->data << " ";
		
		p = p->next;
		count--;
	}
	cout << endl;*/

	while (length > 2)//保留最后两位
	{
	
		if (p == space)//如果p指针指向头结点,则移动p指针指向下一个结点的位置
		{
			p = p->next;
		}
		if (count == 3)//当报数报到3时就删除对应结点
		{
			Node<DataType>*q = p;
			p = p->next;
			//cout << "删除元素:" << q->data << endl;
			Delete_elem(q);
			//cout << "链表:";
			//Display();
			count = 1;
		}
		if (p == space)//针对删除last指向的结点后,指针p指向头结点,故需要将p移到下一个结点的位置
		{
			p = p->next;
		}
		else{
			p = p->next;
			count++;
		}

	}
}

void main(){

	CirculateList<int> head;
	for (int i = 1; i <= 41; ++i){
		head.Insert(i);//插入元素
	}
	cout <<"链表长度:"<< head.GetLength() << endl;
	cout << "链表:";
	head.Display();
	//报数删除
	head.CirculateDelete();
	head.Display();//打印最后剩下的元素

	system("pause");
}

最终效果:

第三行就是最终留下来的两个人的最初位置


 感谢

        通过学习广大学者的博客,自己最终写了这么一点东西,真心感谢。




心得

学习永无止境,前人的智慧仍然值得我们深思。

关于循环链表,个人只学习了浅薄知识,文中有问题的地方还望多多指出,第一次写博客,多多包涵。

  • 6
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值