一.实验目的:
理解线性表的基本逻辑结构,完成链表及循环链表的实现
通过实验进一步理解线性表的逻辑结构和存储结构,提高使用理论知识指导解决实际问题的能力,熟练掌握链表的实际应用。
二.实验内容:
题目: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的上面终止,否则会多输入一次却没存储,没任何意义。
第三个问题:链表的类型选取
我刚开始选取的是无头节点的表尾插入,但是之后发现如果是这样只能选取一个指针,对于删除结点的困难很大,之后选取了带头节点的双指针来删除结点,很方便。
第四个问题:每次出队删除结点,如何记录下他的的顺序
刚开始我想要再建立一个链表来实现发现有点难度,容易搞混,后来想到可以存在数组里面,这样很简单就能保存出队顺序。