南昌航空大学实验报告
课程名称: 数据结构与算法 实验名称: 实验二 线性表的链式存储结构
班 级: XXX 学生姓名: XXX 学号: XXXXX
指导教师评定: XXX 签 名: XXX
一、实验目的
本实验是有关线性表的链式存储结构的应用,通过C语言中提供的结构指针来存储线性表,利用malloc函数动态地分配存储空间。
通过对本实验的学习,可以理解线性表在链序存储结构下的操作方法。
二、实验内容
设计一个程序求出约瑟夫环的出列顺序。约瑟夫问题的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈,每个人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m 值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直到所有人全部出列为止。例如,n=7,7个人的密码依次为:3,1,7,2,4,8,4,m的初值取6,则正确的出列顺序应为6,1,4,7,2,3,5。要求使用单向循环链表模拟此出列过程。
三、程序分析
约瑟夫环的大小是变化的,因此相应的结点也是变化的,使用链式存储结构可以动态的生成其中的结点,出列操作也非常简单。用单向循环链表模拟其出列顺序比较合适。
用结构指针描述每个人:
struct Joseph
{int num;/*环中某个人的序号*/
int secret;/环中某个人的密码*/
struct Joseph *next;/指向其下一个的指针*/};
1)初始化约瑟夫环:
调用函数struct Joseph *creat()生成初始约瑟夫环。在该函数中使用head 指向表头。输入序号为0时结束,指针p1指向的最后结束序号为0的结点没有加入到链表中,p2 指向最后一个序号为非0 的结点(最后一个结点)。
2)报数出列:
调用函数voil sort(struct Joseph * head,int m),使用条件p1->next!=p1判断单向链表非空(循环链表是否只有一个结点),使用两个指针变量p1和p2,语句p2=p1;p1=p1->next;移动指针,计算结点数(报数);结点p1出列时直接使用语句:p2->next=p1->next,取出该结点中的密码作为新的循环终值。
四、程序源代码
过程见后续,不想看过程的直接拉到底即可。
设计思路及过程
通过阅读题目,可以得到以下信息:
1、关于线性表的链式表示和实现
2、用malloc函数动态地分配存储空间
3、使用单向循环链表模拟
第一点链式表示,定义的结构体有一个指向下一结点的指针。
结点的描述与实现
C语言中用带指针的结构体类型来描述
typedef struct Lnode
{ ElemType data; /*数据域,保存结点的值 */
struct Lnode *next; /*指针域*/
}Lnode,*LinkList; /*结点的类型 */
以上为ppt所示,加上几个变量后可直接采用,如下:
typedef struct lnode
{
int num;//序号
int password;//密码
int sign;//是否已出表的标志
struct lnode *next;
}lnode, *linklist;
而第二点和第三点均体现在链表的创建函数(lnode *create(int n))中。
动态分配
p=(LNode*)malloc(sizeof(LNode));
函数malloc分配了一个类型为LNode的结点变量的空间,并将其首地址放入指针变量p中。
动态地建立单链表的常用方法有如下两种:头插入法,尾插入法。
这块我选择了尾插入法。该方法是将新结点插入到当前链表的表尾,使其成为当前链表的尾结点。
LNode *create_LinkList(void) /* 尾插入法创建单链表,链表的头结点head作为返回值 */
{ int data ;
LNode *head, *p, *q;
head=p=(LNode *)malloc(sizeof(LNode));
p->next=NULL; /* 创建单链表的表头结点head */
while (1)
{ scanf(“%d”,& data);
if (data==32767) break ;
q= (LNode *)malloc(sizeof(LNode));
q–>data=data; /* 数据域赋值 */
q–>next=p–>next; p–>next=q; p=q ;/*钩链,新创建的结点总是作为最后一个结点*/
}
return (head);
}
以上为单链表尾插入法建表,而第三点中的单向循环链表,则是在单链表的基础上,让表尾最后一个结点(z)的指针指向头结点(head),即(z->next=head;)。
(循环链表(Circular Linked List):是一种头尾相接的链表。其特点是最后一个结点的指针域指向链表的头结点,整个链表的指针域链接成一个环。)
lnode *create(int n)
{
lnode *head,*p,*z;
head=(lnode *)malloc(sizeof(lnode));
z=head;
for(int i=1;i<=n;i++)
{
p=(lnode *)malloc(sizeof(lnode));
p->num=i;//赋编号
printf("第%d个人手中的密码为:",i);
scanf("%d",&(p->password));//赋密码
p->sign=1;//赋标志,初始为1,可读,若为0,则跳过,表示已出表
p->next=z->next;//添加的p结点的指针勾连到head的指针
z->next=p;//head的指针指向p
z=p;//尾结点变更
}
z->next=head;
return head;
}
然后根据实验要求编写即可。
源代码:
#include
#include
typedef struct lnode
{
int num;//序号
int password;//密码
int sign;//是否已出表的标志
struct lnode *next;
}lnode, *linklist;
lnode *create(int n)
{
lnode *head,*p,*z;
head=(lnode *)malloc(sizeof(lnode));
z=head;
for(int i=1;i<=n;i++)
{
p=(lnode *)malloc(sizeof(lnode));
p->num=i;//赋编号
printf("第%d个人手中的密码为:",i);
scanf("%d",&(p->password));//赋密码
p->sign=1;//赋标志,初始为1,可读,若为0,则跳过,表示已出表
p->next=z->next;//添加的p结点的指针勾连到head的指针
z->next=p;//head的指针指向p
z=p;//尾结点变更
}
z->next=head;
return head;
}
void run(lnode *head,int n,int m)
{
lnode *p;
int i;
p=head;
while(n>=1)
{
i=1;
while(i<=m)
{
p=p->next;//后移一位
if(p->sign==1)i++;//标志为1,读
}
printf("%d ",p->num);
m=p->password;//获得新的m值
p->sign=0;//出列后,赋0,不读
n--;
}
}
int main()
{
int n,m;//n个人,数到m的人出局
linklist head;
printf("共有几个人:n=");
scanf("%d",&n);
if(n<=0)printf("n的数值存在问题!");
printf("初始报数上限值:m=");
scanf("%d",&m);
if(m<=0)printf("n的数值存在问题!");
head=NULL;
head=create(n);
printf("出列顺序:");
run(head,n,m);
printf("\n");
return 0;
}
运行结果:
要求中的例子:例如,n=7,7个人的密码依次为:3,1,7,2,4,8,4,m的初值取6,则正确的出列顺序应为6,1,4,7,2,3,5。