一、问题描述
设编号为1,2,3,……,n的n(n>0)个人按顺时针方向围坐一圈,m为任意一个正整数。从第一个人开始顺时针方向自1起顺序报数,报到m时停止并且报m的人出列,再从他的下一个人开始重新从1报数,报到m时停止并且报m的人出列。如此下去,直到所有人全部出列为止。要求设计一个程序模拟此过程,对任意给定的m和n,求出出列编号序列。
二、实现思路
本程序采用顺序表来实现约瑟夫环问题,求出出列编号序列。顺序表的结构体类型中含有data[MaxSize]、length,数组元素的值为每个人在约瑟夫环中的排序,length存放当前约瑟夫环中的人数。其中,定义顺序表基本运算函数CreateSqList(PSqList &L, int n)、ListDelete(PSqList &L, int i),分别用来建立顺序表、删除顺序表第i个元素。另外,约瑟夫序列算法的具体实现用函数Josephus(PSqList &L, int k, int m)来实现。在该函数中,将逻辑序号转换为物理序号,再利用公式kl = (kl + m - 1) % i()来求出出列人的物理序号即数组下标,删除出列的数组元素,直到数组元素全部都出列。
三、解题代码
#include<iostream>
#include<malloc.h>
#define MaxSize 200 //约瑟夫环人数上限
using namespace std;
typedef int ElemType;
/*声明顺序表结构类型*/
typedef struct
{
ElemType data[MaxSize]; //存放编号的数组
int length; //当前约瑟夫环中的人数
}SqList, *PSqList;
/*建立顺序表*/
void CreateSqList(PSqList &L, int n)
{
L = new SqList;
int i = 0;
while (i < n)
{
L->data[i] = i + 1; //将每个人的逻辑序号的值存放到数组中
i++;
}
L->length = n; //存放约瑟夫环的人数
}
/*删除顺序表第i个元素*/
bool ListDelete(PSqList &L, int i)
{
if (i<1 || i>L->length + 1) //参数i错误时返回false
{
cout << "提供的删除位置i不合法!";
return false;
}
i--; //将顺序表逻辑符号转换为物理序号
for (int j = i; j < L->length; j++)
{
L->data[j] = L->data[j + 1];
}
L->length--;
return true;
}
/*约瑟夫序列算法*/
void Josephus(PSqList &L, int k, int m)
{
int kl = k - 1; //kl为第k个人的数组下标
cout << "约瑟夫环的出列编号序列:";
for (int i = L->length; i > 0; i--)
{
kl = (kl + m - 1) % i; //出列人的物理序号即数组下标
cout << L->data[kl] << " ";
ListDelete(L, kl + 1); //删除出列的数组元素
}
}
int main()
{
PSqList L = NULL; //创建约瑟夫环
int n = 0; //环中人数
int m = 0; //出列间隔
int k = 0; //起始序号
cout << "请输入约瑟夫环的环中人数:";
cin >> n;
cout << "请输入约瑟夫环的出列间隔:";
cin >> m;
cout << "请输入约瑟夫环的起始序号:";
cin >> k;
CreateSqList(L, n); //建立顺序表
Josephus(L, k, m); //约瑟夫序列算法
return 0;
}
四、运行结果
在约瑟夫序列算法的具体函数Josephus(PSqList &L, int k, int m)中,for循环每次使顺序表的长度减1,直到其为0,执行次数为约瑟夫环中的人数n,则该函数的时间复杂度为O(n)。另外,定义了变量kl,k,m,则空间复杂度为O(1)。