西安交通大学数据结构上机报告之约瑟夫环问题仿真

西安交通大学数据结构上机报告之约瑟夫环问题仿真
西安交通大学数据结构上机报告之约瑟夫环问题仿真

1、约瑟夫环问题仿真

设编号为1,2,…,n(n>0)个人按顺时针方向围坐一圈,每人持有一个正整数密码。开始时任意给出一个报数上限m,从第一个人开始顺时针方向自1起顺序报数,报到m时停止报数,报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人起重新自1报数;如此下去直到所有人全部出列为止。

(1)问题分析

可利用单循环链表解决此问题,建立一个单循环链表,依次录入密码,然后从第一个节点出发,连续略过N-1个结点,将第N个节点从链表中删除,并将第N个结点的密码作为新的m值,接着从下一个结点开始,循环此过程,直至链表为空。

(2)数据结构设计

此程序实现的方法是建立一个单循环链表,然后对循环链表进行相关操作,模拟整个报数及出列过程。

主要为以下三个步骤:

1.建立一个具有n个链结点,无头结点的循环链表

2.确定第1个报数人的位置

3.不断地从链表中删除链结点,直到链表为空

首先,建立建立一个单循环链表,将每个人的信息用一个结点存储,包括三个信息,一是他的编号num,二是他持有的密码key,三是

指向下一结点的指针。其中编号是一开始按照相邻顺序编号的,密码可以设置,需要输入数值。

第一次报数默认为从1 号开始报数,需设置报数上限值m ,报m 的人出列,然后从出列的人后面一个人开始下一轮报数,且报数上限值m变为出列的人持有密码。

即第一次由头指针head 开始向后数到第 N-1 个结点,删掉第N-1 个结点的后继结点,即第 N 个结点,这时第 N-1 个结点直接指向第 N+1 个结点,同时让头指针head也指向第 N+1 个结点。因为下一轮报数时将从第 N+1 个结点开始,即还是head指向的结点开始。

第二轮报数首先将m值修改为刚刚被删除结点的持有密码,即m=q->key 再进行和第一轮报数相同的操作。

以后每轮报数如次反复执行,直到所有结点被删除结束。

且在此过程中,根据m值的不同,将分别对m=1与m=其他值的情况进行讨论处理。

程序主要过程可由以下三个函数实现:

1)ERROR()2)struct L *creat(int N)3)struct L

*LisDelete(struct L *head,int m,int N)

(3)算法设计与实现

#include <>

#include <>

#define NULL 0

#define LEN sizeof(struct L)

struct L

{

int num;

int key;

struct L *next;

};

int n;

int i=0;

ERROR()

{

printf("ERROR!\n");

printf("所用的密码必须还正整数而且m>0 N>0!!!\n"); }

struct L *creat(int N)

{

struct L *head;

struct L *p1,*p2;

n=0;

p1=p2=(struct L *)malloc(LEN);

printf(" 请输入第1个人手里的密码:"); scanf("%d",&p1->key);

p1->num=1;

head=NULL;

while(p1->num<=N)

{

n=n+1;

if(n==1) head=p1;

else p2->next=p1;

p2=p1;

p1=(struct L *)malloc(LEN);

if (n==N)

{

printf(" 最后请输入0来结束密码录入:");

}

else

{

printf(" 请输入第%d个人手里的密码:",n+1); }

scanf("%d",&p1->key);

p1->num=n+1;

}

p2->next=head;

return(head);

}

struct L *LisDelete(struct L *head,int m,int N) {

struct L *p,*q;

int j=0,k=0;

q=p=head;

i=i+1;

if(p->next==head) return(p);

if(m==1)

{

while(knext;k=k+1;}

printf("%i 第%d个人退出.\n",i,q->num);

m=q->key;

p->next=q->next;

head=q->next;

free(q);

}

else

{

while(j

{

p=p->next;

j=j+1;

}

q=p->next;p->next=q->next;

printf("%d 第%d个人退出.\n",i,q->num); m=q->key;

head=p->next;

free(q);

}

N=N-1;

LisDelete(head,m,N);

}

main()

{

struct L *head;

struct L *p;

int m,N;

printf("*************************************************** ***\n");

printf("约瑟夫环:\n");

printf(" 编号为1,2,3,4…,n的n个人按顺时针方向围坐一圈,每人\n");

printf("持有一个密码(正整数).一开始任选一个正整数作为报数的上\n");

printf("限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m\n");

printf("时停止.报m的人出列,将他的密码作为新的m值,从他在顺时针\n");

printf("方向上的下一人开始重新从1报数,如此下去,直到所有人全部\n");

printf("出列为止.编程打印出列顺序.\n");

printf("*************************************************** ***\n");

printf(" 输入人数:");

scanf("%d",&N);

printf(" 输入处值m:");

scanf("%d",&m);

printf("***********************************\n");

if (m<=0||N<=0)

{

ERROR();

}

else

{

printf(" 过程:\n");

head=creat(N);

printf("*******************************\n"); printf(" 结果:\n");

p=LisDelete(head,m,N);

printf("%d 第%d个人退出.\n",N,p->num);

}

printf("***********************************\n"); }

(4)运行结果及分析

运行结果如图所示:

分析:

该算法的主要部分是单循环链表,在建立时的时间复杂度为O(N),删除时时间耗费在逐个数元素上,而计数的个数由m确定,m由删除成员的密码确定,m的初值人为确定,最后出列的成员的的密码将不起作用,故时间复杂度为O(m*N)。

综合建立和删除,算法的时间复杂度为O(m*N)。

本算法的不足之处是:当m、N取较大数值时,将无法在短时间内得出结果,影响工作的效率。

因此必要时需进行改进,减小时间复杂度,提高算法的效率。

相关主题
相关文档
最新文档