【数据结构】基于数组和循环队列解决约瑟夫问题

约瑟夫问题是一个经典的问题,下面来介绍约瑟夫问题及其变体的解决方法。

 


1. 约瑟夫问题

设有编号为1、2、3、…、n的n个人围成一个圈,从第一个人开始报数,报到m的人出圈,再从他的下一个人起从新报数,报到m的人出圈,如此下去,直到所有人全部出圈为止。给定任意的n和m,设计算法求n个人出圈的次序。
 


1.1  解决方案

我们尝试使用基本的数组来解决这一问题。

1.2 代码

//约瑟夫问题

#include<iostream>
#include<cstdio>

using namespace std;

const int N = 100;

void Josephus(int a[], int n, int m)
{
	int k = 0;         //记录出局的人数
	int num = -1;  //正在报数的人编号
	while (k < n)
	{
		for (int i = 0; i < m;)
		{
			num = (num + 1) % n;
			if (a[num] != 0)
				i++;
		}
		cout << a[num] << endl;
		a[num] = 0;
		k++;
	}
	return;
}

int main()
{
	int n, m;
	scanf("%d %d", &n, &m);
	int a[N] = { 0 };
	for (int i = 0; i < n; i++)
	{
		a[i] = i + 1;
	}
	Josephus(a, n, m);
	return 0;
}



使用数组的解决方案,代码相当简洁,虽然可读性差了一点。那么,如果开始报数的不是1号,而是由输入决定的p号呢?下面我们来引入约瑟夫问题的变体。

 


2. 约瑟夫问题变体

n个小孩围坐成一圈,并按顺时针编号为1、2、3、…、n,从编号为p的小孩开始报数,由1报到m,报到m的人出圈,再从他的下一个人起从新报数,报到m的人出圈,如此下去,直到所有人全部出圈为止。请按出去的先后顺序输出小孩的编号。


2.1 解决方案

我们这次尝试使用循环队列来解决问题

2.2 代码

//用循环队列解决约瑟夫问题

#include<iostream>
#include<cstdio>
#include<queue>

using namespace std;

void Josephus(int n, int p, int m)
{
	//初始化编号
	queue<int> arr;
	for (int i = 0; i < n; i++)
	{
		arr.push((p - 1 + i) % n + 1);
	}

	int count = 1; //记录队头的报号
	int data;
	while (arr.size() != 0)
	{
		data = arr.front(); //取出队头的数据
		arr.pop();
		if (count == m) //如果报到m
		{
			printf("%d ", data);
			count = 1;
			continue;
		}
		arr.push(data);
		count++;
	}

	return;
}

int main()
{
	int n, p, m;
	printf("Input n ,p and m : ");
	while (scanf("%d %d %d", &n, &p, &m) != EOF)
	{
		if (n == 0 && p == 0 && m == 0)
		{
			printf("Over!\n");
			break;
		}
		printf("Data in turn : ");
		Josephus(n, p, m);
		printf("\nInput n ,p and m : ");
	}

	return 0;
}

 


3. 拓展

上面我们使用了数组、循环队列来解决约瑟夫问题。不管采用什么样的数据结构,解决的算法都是一样的。而程序是由数据结构和算法组成的,这也是数据结构课程中最开始就强调的概念。解决约瑟夫问题还可以尝试使用循环链表来解决,实现思想与循环队列类似,但是实现难度要高得多,这也说明了STL极大地降低了我们解决问题的难度。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值