链表——约瑟夫环

南昌航空大学实验报告

课程名称:   数据结构与算法 实验名称:  实验二 线性表的链式存储结构      

班   级:     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。

链表——约瑟夫环_第1张图片

你可能感兴趣的:(c++,链表)