约瑟夫环
1.实验目的
本次实验意在通过对循环链表的构建删除等操作加强同学对单链表.循环链表的认识和使用,提高同学对结构体的自定义以及指针应用等方面的理解。
2.实验内容与要求
① 问题描述:编号为1到n的n个人,按顺时针方向围成一个环(循环单链表),每人都持有一个密码(正整数)。任选一个正整数作为报数的上限(设为m),从第一个人开始按顺时针方向从1开始顺序报数,当报到m时,暂停报数并输出其标识(位序),同时将报数为m的人删除,并将他的密码作为新的m值,继续从1开始重新报数,直到m并输出。如此重复,直到全部人员都从队列中输出。编写程序,求出该出列顺序。
② 利用循环单链表实现这个过程,单链表的结点约定如下:
Typedef struct LNode {
int IDCode; // 人员标识
int PWD; // 人员密码
struct Lnode *next; // 指针域
} LNode, *LinkList;
其中,L为不带头结点的单链表的头指针,mi是第i个人的密码(1≤i≤n)。请按这个约定编程。
③ 测试数据:20≤n≤30
要求:参数n和m从键盘上输入,同时输入每个人的密码,存放在数组PWD中,利用这些参数构建单链表并完成出列顺序的输出。输入的次序为人员标识(位序)。
④ 主程序约定如下:
main () { //以下仅仅是描述,请大家按照要求完善代码
int PWD[30]; //定义密码数组
printf(“输入人员数n”);
scanf(%d,n);
printf(“输入初始密码m”);
scanf(%d,m);
createJoseph( int n , int PWD); //建立Joseph环,返回指向最后一个结点的指针p
printIDCode(LinkList P , int m); //输出每个人员的编号,当m大于n时,应该取n的模
printf(“以上是张三的运行结果。”);
}
⑤ 提升练习
- 构造顺序文件存放人员密码,根据n读取前n个数据作为密码,实现以上功能;
- 最后建立的p结点的指针,指向了前面的某一个元素结点(未必是第一个),请按上述要求,输出环中的所有人员标识符。
- 实验过程与结果
实验环境:codeblock
完整代码:
#include <stdio.h>
#include <stdlib.h>
#include<time.h>
typedef struct LNode {
int IDCode; // 人员标识
int PWD; // 人员密码
struct Lnode *next; // 指针域
}LNode, *LinkList;
//构造一个空的单链表
LNode *InitJoseph(LinkList L)
{
if(L==NULL)
{
printf("初始化单链表错误!\n");
exit(1);
}
else
L->next=L;
return L;
}
//插入人员密码构造循环单链表
LNode *createJoseph(int n,int PWD,LinkList L)
{
LinkList p=L,q;
int i=1;
if (n==1)
{
p->IDCode=n;
p->PWD=PWD;
p->next=p;
return p;
}
else
{
q=(LinkList)malloc(sizeof(LNode));
q->IDCode=n;
q->PWD=PWD;
q->next=p->next;
p->next=q;
return q;
}
}
//定义遍历函数输出构造好的循环链表的数据
void TraverseList(LinkList L, int n)
{
int i=0;
LinkList p=L;
printf("参与的人的编号为:\n");
while (i<n)
{
printf("%d\t",p->IDCode);
p=p->next;
i++;
}
p=L;
printf("\n参与人的对应密码为:\n");
while (i>0)
{
printf("%d\t", p->PWD);
p=p->next;
i--;
}
printf("\n");
}
//定义删除函数
void printIDCode(LinkList L, int n, int PWD)
{ int i,m;
LinkList p=L,q;
m=PWD;
while(n>0)
{
if(m>n)
m=m%n;//求余m,减少运算次数
if(m==1)
for(i=1;i<n;i++)
p=p->next;
for(i=1;i<m-1;i++)//寻找要删除的成员的前一个成员,找到后令p指向
{
p=p->next;
}
q=p->next;//q为要删除的成员
p->next=q->next;
printf("删除 %d 号成员\t该成员持有的密码为 %d 号\n",q->IDCode,q->PWD);
m=q->PWD;
free(q);
p=p->next;
n--;
}
}
//定义随机指针函数
void random(LinkList L,int n,int m)
{
int i,t;
printf("\n生成一个指针T随机指到第t个人:\n");
srand((unsigned)time(NULL));
for(i=20;i<=30;i++)
t=(rand()%(30-20))+20;
printf("生成的随机数为\t%d",t);
for(i=1;i<t;i++)
L=L->next;
printf("此时游戏从第 %d 号成员开始\n该成员持有的密码为 %d 号\n",L->IDCode,L->PWD);
printIDCode(L,n,m);
printf("\n******************以上是随机指针测试结果*****************\n");
}
void savecode(int n,int M[])
{
FILE *fp;
char filename[20];
int i;
printf("请输入要保存的文件名称:\n");
scanf("%s",filename);
if((fp=fopen(filename,"wb"))==NULL)//打开输出文件
{
printf("cannot open file:\n");
return;
}
for(i=0;i<n;i++)
{
if(fwrite(&M[i],sizeof(int),1,fp)!=1)//数组向磁盘文件写入
printf("file write error!\n");
}
printf("保存文件成功!!!");
fclose(fp);
}
void readcode(int n,int M[])
{
FILE *fp;
char filename[20];
int i;
printf("请输入读入的文件名:");
scanf("%s",filename);
if((fp=fopen(filename,"rb"))==NULL)//以只读方式打开二进制文件
{ printf("can not open file\n");
fclose(fp);
}
for(i=0;i<n;i++)
fread(&M[i],sizeof(int),1,fp);//磁盘文件向数组读入
printf("读入数据成功!!!!");
fclose(fp);
}
void main()
{int i,n,m;
int c;
char filename[20];
LinkList p,L;
int PWD[30];//定义密码数组
printf("请输入参与人员人数n:\n");
scanf("%d",&n);
printf("请输入初始密码m:\n");
scanf("%d",&m);
printf("\n1-请依次输入各成员密码");
printf("\n2-创建约瑟夫环");
printf("\n3-文件存储约瑟夫环密码");
printf("\n4-从文件中读取各成员密码");
printf("\n5-进行普通删除操作");
printf("\n6-进行随机生成删除操作");
printf("\n7-输出所有成员编号及该成员对应的密码信息");
while(1)
{ printf("\n请输入你想进行操作的序号:\n");
scanf("%d",&c);
switch(c)
{
case 1:
{
printf("请依次输入各成员密码:\n");
for(i=0;i<n;i++)//构造密码数组时候n可以换成其他数比如30;
scanf("%d",&PWD[i]);
}
break;
case 2:
{
L=((LinkList)malloc(sizeof(LNode)));
L=InitJoseph(L);
p=L;
for(i=0;i<n;i++)
{
p=createJoseph(i+1,PWD[i],p);//建立Joseph环,返回指向最后一个结点的指针p
}
printf("约瑟夫环构建成功!!!");
L=p->next;//此时L指向第一个节点
}
break;
case 3:
{
savecode(n,PWD);//构造文件存放密码,密码数据可以不是n个
}
break;
case 4:
{
readcode(n,PWD);//读取前n个数作为密码;
}
break;
case 5:
{
printIDCode(L,n,m);
printf("\n******************以上是程序测试结果*****************\n");
}
break;
case 6:
{
random(L,n,m);
}
break;
case 7:
{
TraverseList(L,n);
}
break;
default: break;
} if((c<1)||(c>7))
break;
}
}
代码运行结果:
实验测试使用数据:m的初值为20;n=7,7个人的密码依次为:3,1,7,2,4,8,4,正确的出列顺序为6,1,4,7,2,3,5。
一次运行程序,进行输入成员密码,保存文件,按密码删除等操作。
可见删除的成员序号为正确结果 6,1,4,7,2,3,5
关闭程序,进行下次运行,演示提升练习。此时文件中已经存放待读取的成员密码信息。
之后进行随机删除操作,注意进行随机删除操作时初始密码为20
(4) 实验结果分析
(5) 实验过程中遇到的问题