数据结构 约瑟夫环
Posted Ice丨shine
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构 约瑟夫环相关的知识,希望对你有一定的参考价值。
目录
一、需求分析
功能需求:
设编号为1,2,…,n(n>0)个人按顺时针方向围坐一圈,每人持有一个正整数密码。开始时任意给出一个报数上限m,从第一个人开始顺时针方向自1起顺序报数,报到m时停止报数,报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人起重新自1报数;如此下去直到所有人全部出列为止。
界面需求:
输入人数n,每个人的密码和报数上限(初始密码)m,输出出列顺序
二、概要设计
建立线性表解决该问题:
(1) 建立表的储存结构,获取初始m的值
(2) 根据m的值查找出列数据元素的位置,删除该数据元素并获取新的m值
(3) 重复(2)直到表空
接口设计
void Ysfring(CycleLinkList L,int n,int m);//约瑟夫环
void InitList(CycleLinkList L);//初始化链表
bool ListInsert(CycleLinkList *L,int i,ElemType e); //在不带头结点的循环单链表中第i个数据元素之前插入新的数据元素e
void ListAppend(CycleLinkList *L,ElemType e); //在非空循环单链表表尾插入一个结点
数据结构设计
typedef struct ElemType//数据元素类型
int location;//位置
int password;//密码
ElemType;
//循环链表
typedef struct CycleLinkList
struct LNode *head;
struct LNode *tail;
CycleLinkList,*CL;
//结点结构
typedef struct LNode
ElemType data;
struct LNode *next;
LNode,*LinkList;
三、详细设计
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct ElemType//数据元素类型
int location;//位置
int password;//密码
ElemType;
//循环链表
typedef struct CycleLinkList
struct LNode *head;
struct LNode *tail;
CycleLinkList,*CL;
//结点结构
typedef struct LNode
ElemType data;
struct LNode *next;
LNode,*LinkList;
bool Equal(ElemType a,ElemType b)
if(a.location == b.location && a.password == b.password)
return true;
else
return false;
void Print(ElemType e)
printf("位置:%d,密码:%d",e.location,e.password);
void Ysfring(CycleLinkList L,int n,int m)//约瑟夫环
LNode *current = L.tail,*s;
ElemType e;
int k=n;
while(n>0)
m = m % n;
if(m == 0) m = n;
int j;
for(j=1;j<m;j++) current = current ->next;//current指向应该出列的结点的前一个结点
s=current->next;//s指向出列结点
current->next=s->next;//让s结点退出链表
printf("第%d个出列的位序是:%d\\n",k-n+1,s->data.location);
m=s->data.password;//得到当前结点的密码
free(s);//结点出列
n--;
L.head=L.tail=NULL;
void InitList(CycleLinkList L)//初始化链表
L.head=L.tail=NULL;
//在不带头结点的循环单链表中第i个数据元素之前插入新的数据元素e
bool ListInsert(CycleLinkList *L,int i,ElemType e)
LNode *p,*s;
if(i<1) return false;
if(i==1)
s = (LNode *)malloc(sizeof(LNode));
s->data = e;
if(!L->head==NULL)
L->head=L->tail=s;//将s插入空表中
else
s->next=L->head;
L->head=s;//将s结点插在表首
L->tail->next=L->head;//形成循环链表
else
p=L->head;
int j=1;
while(p!=L->head&&j<i-1)//找到需要插入结点的位置
p=p->next;
j++;
if(p==L->head)//插入失败返回false
return false;
else
s = (LNode *)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s;
return true;
//在非空循环单链表表尾插入一个结点
void ListAppend(CycleLinkList *L,ElemType e)
LNode *s;
s=(LNode *)malloc(sizeof(LNode));
s->data=e;
s->next=L->tail->next;
L->tail->next=s;
L->tail=s;
int main()
CycleLinkList L;
InitList(L);
int n,m;
ElemType e;
printf("《约瑟夫环》\\n");
printf("请输入人数:\\n");
scanf("%d",&n);
if(n<1) //判断输入是否合法
printf("请输入正整数!");
return 0;
printf("请输入第1人的密码:\\n");
scanf("%d",&e.password);
if(e.password<1)//判断输入是否合法
printf("请输入正整数!");
return 0;
e.location=1;
ListInsert(&L,1,e);
int i;
for(i=2;i<=n;i++)
printf("请输入第%d个人的密码:\\n",i);
scanf("%d",&e.password);
if(e.password<1)
printf("请输入正整数!");
return 0;
e.location=i;//输入每个人的密码并插入表尾
ListAppend(&L,e);
printf("请输入初始密码:");
scanf("%d",&m);
Ysfring(L,n,m);
return 0;
四、调试分析
- 在建表过程中,插入的第一个结点与插入其他结点有所不同
- 删除结点时,为了明确删除结点的前驱,设置current指针记录,该指针初始指向表尾
- 为过滤不必要的循环次数,使用模运算,提高效率
- 由于使用了模运算,算法的复杂度为O(n²)。
五、用户手册
1、本程序经code::blocks编译,运行环境为win10
2、进入演示程序后,将显示如下的界面
3、输入人数和各个人所拥有的密码,再输入初始密码后输出出列顺序,如下图:
六、测试结果
编译Ysfring.c后执行,输入人数5,密码1-5号人分别为5,4,3,2,1,结果如下:
输入小于1的人数或密码输出提示信息并退出。
以上是关于数据结构 约瑟夫环的主要内容,如果未能解决你的问题,请参考以下文章