一、实验目的
能够熟练掌握线性表的基本操作在顺序和链式两种存储结构上的实现,进一步理解线性表的逻辑结构和存储结构,提高使用理论知识指导解决实际问题的能力。
二、实验题目
约瑟夫环问题
编号为1,2,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值max,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报max的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序求出出列顺序。
利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。
三、问题分析
1.数据分析
报数上限值为m,报到m的人的密码作为新的m,m改变
有n个人参与报数
2.运算分析
先用尾插法创建一个带头节点的循环单链表,创建的同时输入编号和对应的密码,为了确认是否创建成功,我们可以将链表遍历出来(show)。
然后我们进入约瑟夫环问题,成员依次报数,每当达到报数上限时,先覆盖上限值,再在循环单链表中删除当前节点,继续新一轮的报数,知道链表中剩下最后一个储存数据的结点。
3.主要算法分析
首先刚开始的次序为1,2,3,...,7,8。从第一个人开始遍历,数到m,也就是第m个人就出去,先打印出列人的序号,再把这个结点从表中删除,继续遍历,继续找到下一个出列的人打印序号。如果用单链表解决的话,就出现了一个问题,遍历到尾结点时,无法继续报数了。所以此时需要用到循环单链表,将尾结点和头部连接起来。这样就能继续进行遍历。重复打印序号、删除结点操作,直到只剩下最后一个人。
四、源程序
#include <stdio.h>
#include <malloc.h>
#define NULL 0
typedef struct Node {
int num;
int password;
struct Node *next;
} Node;
//建立循环链表
Node *CreatFromTail(int n) {
Node *L, *r;
Node *s;
int k = 0;
L = (Node *)malloc(sizeof(Node));
L->next = L;
r = L;
while (k < n) {
int num;
int password;
printf("请输入第%d个人的编号和密码\n", k + 1);
scanf("%d%d", &num, &password);
//申请新节点
s = (Node *)malloc(sizeof(Node));
s->num = num;
s->password = password;
//尾插法启动
r->next = s;
r = s;
k++;
}
r->next = L->next;
return L;
}
//进行报数
void Game(Node *L, int m, int n) {
//定义指针去寻找
Node *p, *r;
p = L;
printf("起立的顺序为\n");
while (n > 0) {
for (int i = 0; i < m - 1; i++) {
p = p->next;
}
r = p->next;
m = r->password;
printf("%d", r->num);
p->next = r->next;
n--;
}
}
int main() {
int m, n;
printf("请输入人数n\n");
scanf("%d", &n);
printf("请输入m的值\n");
scanf("%d", &m);
Node *L = CreatFromTail(n);
Game(L, m, n);
}