约瑟夫环的数组和链表分别实现
问题描述
约瑟夫环问题的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直到所有人全部出列为止。试设计一个程序求出出列顺序。用顺序表和链表结构分别实现。
测试数据
(1)n=7,7个人的密码依次为:3,1,7,2,4,8,4,首先m值为6(正确的出列顺序应为6,1,4,7,2,3,5)。
(2)n=9,9个人的密码依次为:4,3,7,1,5,6,3,8,2,首先m值为4
(3)自己任选其它一组测试数据。
代码
顺序表结构
主要是用索引%人数m得出真实的位置
#include <stdio.h>
#include <malloc.h>
#define MAXSIZE 100
typedef struct Cir *list;
struct Cir{
int pw[MAXSIZE]; //存储密码
int isdeq[MAXSIZE]; //是否出队 0不出队,1出队
int Last; //表示表尾的索引
};
void init(list &l){
l = (list)malloc(sizeof(struct Cir));
l -> Last = -1;
}
void append(int X, list &l){
if(l -> Last == MAXSIZE-1){
printf("表满");
return;
}
l->pw[l->Last+1]=X;
l->isdeq[l->Last+1]=0; //出队全部初始为0
l->Last++; //表长加一
return;
}
void add(int n, list &l){
init(l);
l = (list)malloc(n*sizeof(struct Cir));
l -> Last = -1;
}
int main(){
list l;
int n;
printf("输入游戏人数n:");
scanf("%d",&n);
//动态扩充顺序表容量
add(n,l);
printf("输入%d个人的密码:",n);
int i;
for(i=0; i<n; i++){
int X;
scanf("%d",&X);
append(X,l);
}
int m;
printf("输入开始第一个报数值m:");
scanf("%d",&m);
int index=-1;
printf("出列顺序:");
while(l->Last >= 0){
while(m > 0){
index += 1;
index %= n;
if(l->isdeq[index] == 1){
continue;
}
if(m ==1){
printf("%d ",index+1);
l->isdeq[index] = 1;
m = l->pw[index];
l->Last--;
break;
}
m--;
}
}
free(l);
return 0;
}
运行截图
链表结构
使用指针遍历单向循环链表,每当报数到m值时,删除当前指针节点,更改m值为删除人的密码,修改指针的为删除节点的前一个节点。
#include <stdio.h>
#include <malloc.h>
typedef struct Lnode *list;
struct Lnode{
int num; //存储序号
int data; //存储密码
struct Lnode *next; //下一跳的节点指针
};
//创建空链表 只有头指针
list init(){
list head = (list)malloc(sizeof(struct Lnode));
head ->next =NULL;
return head;
}
//添加节点方法
list add(list &l,int num){
//创建头结点
list head = init();
//创建指针,指向头结点
list flag = head;
int i;
//往头结点后插入num个节点
for(i=0;i<num;i++){
flag->next = (list)malloc(sizeof(struct Lnode));
//创建节点成为头结点的下一跳
flag = flag->next;
//给节点编序号,并输入密码赋值给data
flag->num = i+1;
scanf("%d",&flag->data);
}
//尾节点的下一跳指向头结点下一跳,形成循环链表
flag ->next = head->next;
//返回头结点
return head;
}
list deleteNode(list &lbefore,int &m){
//创建p指针删除节点的前一个节点,q指向删除节点
list p,q;
p=lbefore;
//找到要删除节点的前一项节点
while(m>1){
p = p->next;
m--;
}
//修改p->q->next 改为 P->next
q = p ->next;
p->next = q->next;
//输出删除节点的序号,更改m值为删除节点的密码 ,释放删除节点
printf("%d ",q->num);
m = q ->data;
free(q);
lbefore = p;
//返回删除节点的前一个节点
return lbefore;
}
game(list &l,int m,int n){
//当人数n等于1时游戏结束
while(n-1){
//删除第m个节点, 传入l是上一个删除节点的前一个节点
deleteNode(l,m);
n--;
}
//输出最后一个人的密码
printf("%d",l->num);
}
int main(){
list l;
int n;
printf("输入游戏人数n:");
scanf("%d",&n);
printf("输入%d个人的密码:",n);
int i;
//插入n个节点和密码
l=add(l,n);
int m;
printf("输入游戏开始的第一个报数值m:");
scanf("%d",&m);
//开始游戏
game(l,m,n);
return 0;
}
运行截图: