一.实验目的:

理解线性表的基本逻辑结构,完成链表及循环链表的实现

通过实验进一步理解线性表的逻辑结构和存储结构,提高使用理论知识指导解决实际问题的能力,熟练掌握链表的实际应用。

二.实验内容:

题目:Josephus环问题

问题描述:

约瑟夫(Joseph)问题的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈。任选一个正整数作为报数上限值m,从第k个人开始按顺时针方向自1开始顺序报数,报到m时停止报数,报m的人出列,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直到所有人全部出列为止。试设计一个程序求出出列顺序。

基本要求:

利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。

三. 实验方案

(一)算法设计思路:

由题知使用带头结点单循环链表,

建立链表:

第一步:建立头节点、然后建立单循环链表。(建立一个输出函数检验是否建立成功)。

       第二步:建立主函数,通过键盘输入人数、报道数,调用单循环链表和建立存放出队顺序的数组。

第三步:建立子函数,通过主函数传来链表的头指针,总人数,报数值和数组(用来存每个人出列顺数),调用子函数的功能

  子函数功能实现:使用两个指针 p(头指针) q(p前一个结点的指针)二者相互交替完成链表结点的删除和空间的释放。在while的语句内循环至只剩一个人,再将其存入最后一位数组中。

第四步:输出数组a的值即是出队顺序。

(二)使用模块及变量的说明(例如结构体的定义、含义)

typedef struct Lnode:(组成链表的结点)

data来标记每个人,

next是指向下一个结点的指针(来存结点的地址)

LNode 是结点的别名,*LinkList是指向结点指针。

LinkList Creat_list(int n)函数:(创建单循环链表)

    L是头指针指向头节点,r是尾指针指向最后一个结点

    s存放申请的结点空间的地址。

    n是传来的总人数,k是来控制循环结束

   

int output(LinkList L):(检查链表是否创建成功)

int main():(调用各个函数)

    n是总人数,m是报道人数

L是头指针(指向头结点)

A[100]是存放出队顺序的数组

void Func(LinkList tail, int n, int m, int* a):(获得出队顺序的函数)

    count是每个人的报数值,flag是死亡人数值

p是头指针(指向头节点的下一个结点),q是指向头节点的指针

i是数组a的下标。

四. 实验步骤或程序(经调试后正确的源程序)

#include<iostream>

#include<string>

using namespace std;



typedef struct Lnode

{

    int data;

    struct Lnode* next;



}LNode,*LinkList;



LinkList Creat_List(int n)

{

    LinkList L = NULL;

    LNode* s, * r = NULL;

    int x;

    cin >> x;

    s = (LNode*)malloc(sizeof(LNode));

    if (s == NULL) {

         cout << "调用失败";

    }

    else

         L = s;

    r = s;

    int k = 1;

    while (1)

    {

         k++;

         s = (LNode*)malloc(sizeof(LNode));

         if (s == NULL) {

             cout << "调用失败";

         }

         else

             s->data = x;



         r->next = s;



         r = s;

         if (k > n)

             break;

         cin >> x;



    }

    r->next = L->next;

    return L;



}

int output(LinkList L)//有头节点输出

{

    while (L->next)

    {

         L = L->next;

         cout << L->data << " ";



    }

    return 0;



}





int main()

{

    void Func(LinkList tail, int n, int m, int* a);

   

    int n,m;

    cout << "总人数:";

    cin >> n;

    LinkList L,H;

    cout << "编号依次为:"<<endl;

    L=Creat_List(n);



    cout << "输入报道的数";

    cin >> m;

    int a[100];

   

    Func(L, n, m, a);

    cout << "出列顺序依次是:";

    for (int i = 0; i < n; i++)

    {

         cout << a[i]<<" ";

    }

 system("pause");

    return 0;

}



void Func(LinkList tail, int n, int m, int* a)

{

    int i = 0;

    int count = 0, flag = 0;//设置报数值和死亡人数值

    LinkList  p= tail->next, q = tail;//定义p为头指针,q为p前一个结点的指针

    while (flag < n - 1) {//死亡人数没有达到n-1

         count++;//报数

         if (count == m) {//满足出圈条件

            

             a[i] = p->data;//记录当前出圈者的编号

             i++;//让数组移向下一个值

             q->next = p->next;//让p的上一个结点的指针指向p的下一个结点

             free(p);//释放当前结点

             p = q->next;//p移动到下一个人

             count = 0;//重新开始报数

             flag++;//死亡人数加一

         }

         else {//不满足就移向下一个人

             q = p;

             p = p->next;

         }

    }

    a[i]= p->data;//记录最后一个人的编号

    free(p);

}

五.程序运行结果(程序运行结果的一些截图)

约瑟夫环问题 python 约瑟夫环问题实验报告_结点

约瑟夫环问题 python 约瑟夫环问题实验报告_链表_02

约瑟夫环问题 python 约瑟夫环问题实验报告_链表_03

六.实验总结(调试过程中遇到哪些问题,怎么解决的)

第一个问题:链表建立不成

    刚开始建立链表循环书中是while用标志来停止循环的,但是题中要求了总人数,所以可以用count来计数。用break停止循环

第二个问题:链表每次都多输入一个数,但输出链表时又没了。

我检查了一下发现是break停的位置不对,应放在cin的上面终止,否则会多输入一次却没存储,没任何意义。

第三个问题:链表的类型选取

    我刚开始选取的是无头节点的表尾插入,但是之后发现如果是这样只能选取一个指针,对于删除结点的困难很大,之后选取了带头节点的双指针来删除结点,很方便。

第四个问题:每次出队删除结点,如何记录下他的的顺序

       刚开始我想要再建立一个链表来实现发现有点难度,容易搞混,后来想到可以存在数组里面,这样很简单就能保存出队顺序。