约瑟夫环(综合性实验)
1. 需求分析
需求:
约瑟夫问题的一种描述是,编号为1, 2, …, n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数, 报到m时停止报数。报m的人出列,将他的密码作为新的m值,位于他顺时针方向上的下一个人开始重 新从1报数,如此下去,直至所有人全部出列。试设计一个程序求出出列顺序。要求: 利用单向循环链表存储结构模拟此过程,按照出列的顺序打印出每个人的编号。
分析:
- 输入的形式和输入值的范围:按照提示将数据放在文件后,输入Y,输出结果生成在文件“ 约瑟夫环结果.txt”中。
- 输出的形式:输出数个int型数字。
- 程序所能达到的功能:
利用单向循环链表存储结构模拟约瑟夫环,按照出列的顺序打印出每个人的编号。 - 测试数据:
两组数据,分别是:
6
3 1 7 2 4 8
1
和
7
3 1 7 2 4 8 4
20
将两组数据放入“约瑟夫环.txt”中,输入Y,程序将运行结果放入文件“约瑟夫环结果.txt”中。
2. 概要设计
- 为了实现程序功能,需要定义顺序栈的抽象数据类型。
ADT List {
数据对象: D={ ai | ai ∈ElemSet, i=1,2,...,n, n≥0 }
数据关系: R1={ <ai-1, ai >|ai-1, ai∈D, i=2,...,n }
typedef struct lnote{ //链表节点
int number; //编号
int data; //密码
struct lnote *next;
}*node,NODE;
typedef struct LIST{ //链表结构
node head;
node tail;
int len;
}list;
基本操作:
初始化操作 InitList( &L )
操作结果:构造一个空的线性表L。
结构销毁操作 DestroyList( &L )
初始条件:线性表 L 已存在。
操作结果:销毁线性表 L。
线性表判空操作 ListEmpty( L )
初始条件:线性表 L 已存在。
操作结果:若 L 为空表,则返回 TRUE,否则返回FALSE。
求线性表的长度 ListLength( L )
初始条件:线性表 L 已存在。
操作结果:返回 L 中数据元素的个数。
求数据元素的前驱 PriorElem( L, cur_e, &pre_e )
初始条件:线性表 L 已存在。
操作结果:若 cur_e 是 L 的元素,则用pre_e 返 回它的前驱,否则操作失败,pre_e无 定义。
求数据元素的后继 NextElem( L, cur_e, &next_e )
初始条件:线性表 L 已存在。
操作结果:若 cur_e 是 L 的元素,则用next_e 返 回它的后继,否则操作失败,next_e 无定义。
求线性表中第i个数据元素 GetElem( L, i, &e )
初始条件:线性表 L 已存在,并且 1≤i≤ListLength(L) 。
操作结果:用 e 返回 L 中第 i 个数据元素的值。
定位函数 LocateElem( L, e, compare( ) )
初始条件:线性表 L 已存在,e 为给定值, compare( ) 是元素判定函数。
操作结果:返回 L 中第 1 个与 e 满足关系 compare( ) 的元素的位序。 若这样的元素不存在,则返回值为 0。
遍历线性表 ListTraverse(L, visit( ))
初始条件:线性表 L 已存在。 visit( ) 为某个访问函数。
操作结果:依次对 L 中每个元素调用 函数visit( )。一旦 visit( )失败, 则操作失败。
线性表置空 ClearList( &L )
初始条件:线性表 L 已存在。
操作结果:将 L 重置为空表。
改变第 i 个数据元素的值 PutElem( &L, i, e )
初始条件:线性表 L 已存在,并且 1≤i≤ListLength(L) 。
操作结果:L 中第 i 个元素赋值 e 。
插入数据元素 ListInsert( &L, i, e )
初始条件:线性表 L 已存在,且 1≤i≤ListLength(L)+1 。
操作结果:在 L 中的第 i 个元素之前插入 新的元素 e,L 的长度增1。
删除数据元素 ListDelete( &L, i, &e )
初始条件:线性表 L 已存在并且非空, 且1≤i≤ListLength(L) 。
操作结果:删除 L中的第 i 个元素,并且用 e 返 回其值,L 的长度减1。
} ADT List
3.具体代码
#include<stdio.h>
#include<stdlib.h>
typedef struct lnote{ //链表节点
int number; //编号
int data; //密码
struct lnote *next;
}*node,NODE;
typedef struct LIST{ //链表结构
node head;
node tail;
int len;
}list;
void createlist(list *p); //创建线性表
void add_node(list *p,int password,int num); //添加节点
void delete_node(list *p,int num); //删除节点
int main()
{
FILE *information,*result;
char judge;
int n,m,sum;
printf("请将数据依次放入文件'约瑟夫环.txt.'\n格式如下:\n人数\n各个人的密码\n初始密码\n");
printf("是否放入?(Y/N)");
scanf("%c",&judge);
while(judge != 'Y'&&judge != 'y')
{
printf("请将数据放入文件'约瑟夫环.txt.'\n");
printf("是否放入?(Y/N)");
getchar();
scanf("%c",&judge);
}
if((information=fopen("约瑟夫环.txt","rt"))==NULL)
{
exit(-1);
}
if((result=fopen("约瑟夫环结果.txt","w"))==NULL)
{
printf("\n无法创建“约瑟夫环结果.txt”!");
exit(-1);
}
for(int sequence = 1;(fscanf(information,"%d",&n))!=EOF;sequence++) //模拟约瑟夫环过程
{
list loop;
createlist(&loop); //创建链表
for(int i = 1;i<=n;i++) //读取数据并写入链表
{
int password;
fscanf(information,"%d",&password);
add_node(&loop,password,i);
}
fscanf(information,"%d",&m); //读取初始上限值
node p;
p = loop.head->next;
int delete_num=1;
fprintf(result,"第%d组数据结果:",sequence);
for(;loop.len != 0;)
{
for(int i = 1;i < m;i++) //报数过程
{
if(p == loop.tail) //报数到链表尾,从头链表再次循环
{
delete_num=1;
p = loop.head->next;
}
else //报数
{
delete_num++;
p = p->next;
}
}
fprintf(result,"%d ",p -> number); //出列
m = p->data; //密码作为新m值
if(p == loop.tail)
{
p = loop.head->next;
}
else
{
p = p->next;
}
delete_node(&loop,delete_num); //删除链表中出列的节点
if(p == loop.head->next) //出列者若为链表尾,则从链表头开始
{
delete_num=1;
}
}
fprintf(result,"\n");
}
printf("结果已生成在“约瑟夫环结果.txt”");
return 0;
}
void createlist(list *p) //创建线性表
{
p->head = malloc(sizeof(NODE));
p->head->next = NULL;
p->tail = p->head;
p->len=0;
}
void add_node(list *p,int password,int num) //添加节点
{
node q;
q = malloc(sizeof(NODE));
q->data = password;
q->number = num;
q->next = NULL;
p->tail->next = q;
p->tail = q;
p->len++;
}
void delete_node(list *p,int num) //删除节点
{
node q,r;
q = p->head;
for(int n = 1;n++ < num;q = q->next);
r=q->next;
q->next=r->next;
if(num == p->len)
{
p->tail = q;
}
free(r);
p->len--;
}