一.实验目的:
理解线性表的基本逻辑结构,完成链表及循环链表的实现
通过实验进一步理解线性表的逻辑结构和存储结构,提高使用理论知识指导解决实际问题的能力,熟练掌握链表的实际应用。
二.实验内容:
题目: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); }
五.程序运行结果(程序运行结果的一些截图)
六.实验总结(调试过程中遇到哪些问题,怎么解决的)
第一个问题:链表建立不成
刚开始建立链表循环书中是while用标志来停止循环的,但是题中要求了总人数,所以可以用count来计数。用break停止循环
第二个问题:链表每次都多输入一个数,但输出链表时又没了。
我检查了一下发现是break停的位置不对,应放在cin的上面终止,否则会多输入一次却没存储,没任何意义。
第三个问题:链表的类型选取
我刚开始选取的是无头节点的表尾插入,但是之后发现如果是这样只能选取一个指针,对于删除结点的困难很大,之后选取了带头节点的双指针来删除结点,很方便。
第四个问题:每次出队删除结点,如何记录下他的的顺序
刚开始我想要再建立一个链表来实现发现有点难度,容易搞混,后来想到可以存在数组里面,这样很简单就能保存出队顺序。