单向循环链表解决约瑟夫问题(C++)

 在大厂面试时,经常会被提出实现约瑟夫问题。那么什么是约瑟夫问题呢?

约瑟夫问题:

        n个人围成一个圆圈,分别编号1~n,从第一个人开始顺时针报数为1,报到第m的时候。令其出列,然后再从下一个人重新开始报数,当从1报到m时,报m的人继续出列,以此重复下去,直到所有人都出列为止,获得所有人出列的序号。

当m=8,n=3时,出列的序号如下所示:

 最终删除的序列为:3、6、1、5、2、8、4、7。

        C++中有一种数据存储结构与这种首尾相连的环形结构极为相似——单向循环链表。对于单向循环链表的详细介绍可见“C++单向循环链表实现 ”。

下面我们将通过单向循环链表去实现约瑟夫问题。

1、首先是链表创建与初始化,这已经是老生常谈的问题了,在这里就不再赘述了,详情可见笔者之前写的有关链表的博客文章。

//节点定义
class node
{
public:
	int data;
	node* next;
};
//链表定义
class list
{
public:
	int size;
	node* head;
};
//链表初始化
list* list_init()
{
	list* L=new list;
	L->size=0;
	L->head=new node;
	L->head->data=NULL;
	L->head->next=L->head;
	return L;
}

2、我们创建的链表里面并不含有任何数据,因此我们还需要通过数据插入函数,将数据插入到链表中。由于约瑟夫问题还涉及到数据删除的问题,因此我们还要提供数据删除函数。为了知道我们的数据是否成功插入或删除,我们需要提供打印链表中数据的函数。

//链表插入数据
void list_insert(list *L,int pos,int num)
{
	node* new_node=new node;
	new_node->data=num;
	new_node->next=NULL;

	node* pcurrent=L->head;
	for (int i = 0; i < pos; i++)
	{
		pcurrent=pcurrent->next;
	}
	new_node->next=pcurrent->next;
	pcurrent->next=new_node;
	L->size++;
}
//删除链表(按值)
void list_delete(list* L,int num)
{
	node* pcurrent=L->head->next;
	int pos=0;
	for (int i = 0; i < L->size; i++)
	{
		if (pcurrent->data==num)
		{
			break;
		}
		pcurrent=pcurrent->next;
		pos++;
	}	
	pcurrent=L->head;
	for (int i = 0; i < pos; i++)
	{
		pcurrent=pcurrent->next;
	}
	pcurrent->next=pcurrent->next->next;
	L->size--;
}
//打印链表
void list_print(list *L,int num)
{
	node* pcurrent=L->head;
	for (int i = 0; i < num; i++)
	{
		if (pcurrent==L->head)
		{
			pcurrent=pcurrent->next;
		}
		cout<<pcurrent->data<<"\t";	
		pcurrent=pcurrent->next;
	}
	cout<<endl;
}

 这些准备工作都完成之后我们就可以开始正式实现约瑟夫问题了,这里我同样将约瑟夫问题的m设为8、n设为3。

const int m=8;
const int n=3;

3、首先是数据1~8插入链表,同时还可以打印检核一下

	//在链表中插入1~8
	list* L=list_init();
	for (int i = 0; i < m; i++)
	{
		list_insert(L,i,i+1);
	}
	cout<<"单向循环链表为:"<<endl;
	list_print(L,m);

4、开始解决约瑟夫问题,这明显是一个条件循环问题,只要链表中还有数据它就会一直循环下去。由于报号的过程是从自身开始的,因此除了自身外,还需要往后数两个数即这三个数分别为:pcurrent、pcurrent->next、pcurrent->next->next。当第三个人报号时,把他剔除。并将下次重新报号的人往后移一位,这样就可以与前面循环起来了。

	//开始解决约瑟夫问题
	cout<<"\n开始循环:"<<endl;
	node* pcurrent=L->head;
	int iter=1;
	while (L->size != 0)
	{
		for (int i = 0; i < 2; i++)
		{
			if (pcurrent == L->head)  //排除头node
			{
				pcurrent=pcurrent->next;
			}
			pcurrent=pcurrent->next;
			if (pcurrent == L->head) //排除头node
			{
				pcurrent=pcurrent->next;
			}
		}
		cout<<"第"<<iter<<"轮删除的数字:"<<pcurrent->data<<endl;
		//node* temp_p=pcurrent;
		list_delete(L,pcurrent->data);
		pcurrent=pcurrent->next;
		iter++;
	}

至此我们就完成了整个约瑟夫问题。

下面展示整体的代码与运行结果:

1、代码

#include <iostream>
using namespace std;
const int m=8;
const int n=3;
//创建单向循环链表
class node
{
public:
	int data;
	node* next;
};
class list
{
public:
	int size;
	node* head;
};
list* list_init()
{
	list* L=new list;
	L->size=0;
	L->head=new node;
	L->head->data=NULL;
	L->head->next=L->head;
	return L;
}
//链表插入数据
void list_insert(list *L,int pos,int num)
{
	node* new_node=new node;
	new_node->data=num;
	new_node->next=NULL;

	node* pcurrent=L->head;
	for (int i = 0; i < pos; i++)
	{
		pcurrent=pcurrent->next;
	}
	new_node->next=pcurrent->next;
	pcurrent->next=new_node;
	L->size++;
}
//删除链表(按值)
void list_delete(list* L,int num)
{
	node* pcurrent=L->head->next;
	int pos=0;
	for (int i = 0; i < L->size; i++)
	{
		if (pcurrent->data==num)
		{
			break;
		}
		pcurrent=pcurrent->next;
		pos++;
	}	
	pcurrent=L->head;
	for (int i = 0; i < pos; i++)
	{
		pcurrent=pcurrent->next;
	}
	pcurrent->next=pcurrent->next->next;
	L->size--;
}
//打印链表
void list_print(list *L,int num)
{
	node* pcurrent=L->head;
	for (int i = 0; i < num; i++)
	{
		if (pcurrent==L->head)
		{
			pcurrent=pcurrent->next;
		}
		cout<<pcurrent->data<<"\t";	
		pcurrent=pcurrent->next;
	}
	cout<<endl;
}
int main()
{
	//在链表中插入1~8
	list* L=list_init();
	for (int i = 0; i < m; i++)
	{
		list_insert(L,i,i+1);
	}
	cout<<"单向循环链表为:"<<endl;
	list_print(L,m);
	//开始解决约瑟夫问题
	cout<<"\n开始循环:"<<endl;
	node* pcurrent=L->head;
	int iter=1;
	while (L->size != 0)
	{
		for (int i = 0; i < 2; i++)
		{
			if (pcurrent == L->head)  //排除头node
			{
				pcurrent=pcurrent->next;
			}
			pcurrent=pcurrent->next;
			if (pcurrent == L->head) //排除头node
			{
				pcurrent=pcurrent->next;
			}
		}
		cout<<"第"<<iter<<"轮删除的数字:"<<pcurrent->data<<endl;
		//node* temp_p=pcurrent;
		list_delete(L,pcurrent->data);
		pcurrent=pcurrent->next;
		iter++;
	}
	system("pause");
	return 0;
}

2、结果

如有错误的地方欢迎指正交流!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值