约瑟夫环的单向循环链表实现和数组实现(C语言)
题目表述:
7-2 约瑟夫环 (13 分)
N个人围成一圈顺序编号,从1号开始按1、2、3…顺序报数,报p者退出圈外,其余的人再从1、2、3开始报数,报p的人再退出圈外,以此类推。 请按退出顺序输出每个退出人的原序号。
输入格式:
输入只有一行,包括一个整数N(1<=N<=3000)及一个整数p(1<=p<=5000)。
输出格式:
按退出顺序输出每个退出人的原序号,数据间以一个空格分隔,但行尾无空格。
输入样例:
在这里给出一组输入。例如:
7 3
输出样例:
3 6 2 7 5 1 4
- 方法一(数组实现)
我用这种方法在提交时出现了运行超时的错误提示,代码如下
//约瑟夫环 数组 运行超时
#include <stdio.h>
#include <stdlib.h>
int main(){
int n,m;//n记录总人数,m记录报到这个数字的人退出
int count = 1;//记录报数,知道count等于m
int number;//记录剩余人数
//输入n,m
//printf("请输入n:");
scanf("%d",&n);
number = n;//number刚开始等于n
//printf("请输入m:");
scanf("%d",&m);
int person[3000] = {0};
//int later[3000] = {0};
int i;//记录循环下标
//初始化数组
for(i = 0; i < n; i++){
person[i] = i+1;
}
//进行报数,0代表退出
int pos = 0;//记录处理下标
int flag = 0;//控制空格输出
//int la = 0;//记录later下标
while(number > 0){
if(person[pos] > 0){
if(count != m){
pos = (pos + 1)%n;//当前处理的数组下标+1
count++;//报数加一
}else{//说明count == m,要删除数据,即置为0
// later[la] = person[pos];//删除之前保存数据
// la++;
//printf("later[%d]=%d\n",pos,later[pos]);
if(flag == 0){
printf("%d",person[pos]);
flag = -1;
}else{
printf(" %d",person[pos]);
}
person[pos] = 0;//0代表退出
count = 1;//count重置为1,重新报数
number--;//不要忘记让数量-1
//printf("number为%d\n",number);
pos = (pos + 1)%n;//当前处理的数组下标+1
}
}else{//说明person[pos] <= 0,说明当前位置的人已经退出
pos = (pos + 1)%n;//直接将当前处理下标+1即可
}
}
// for(i = 0; i < n; i++){
// if(i == 0){
// printf("%d",later[i]);
// }else{
// printf(" %d",later[i]);
// }
// }
return 0;
}
2.方法二(单向循环链表实现)
代码如下
#include <stdio.h>
#include <stdlib.h>
typedef struct node{
int data;
struct person *next;
}person;
person *initLink(int n);
void findAndKillM(person *head, int m);
int main(){
int n,m;
scanf("%d",&n);
scanf("%d",&m);
person *head = initLink(n);
findAndKillM(head,m);
return 0;
}
person *initLink(int n){
person *head ,*tail, *p;
head = (person *)malloc(sizeof(person));
head->data = 1;
head->next = NULL;
tail = head;//尾指针刚开始指向首结点
int i;
for(i = 2; i <= n; i++){
p = (person *)malloc(sizeof(person));
p->data = i;
p->next = NULL;
tail->next = p;
tail = p;
}
tail->next = head;//首尾相连
return head;
}
void findAndKillM(person *head, int m){
person *tail = head;
//找到链表第一个结点的上一个结点,为删除做准备
while(tail->next != head){
tail = tail->next;
}
person *p = head;//p指向要处理的结点
int i = 0;//循环下标
int flag = 1;//格式化输出控制
if(p->next == p){//如果只有一个人
printf("%d",p->data);
free(p);
}else{
while(p->next != p){
//从第一个结点开始报数,找到报数为m的人,并要记录报m-1的人位置,方便删除m的位置
for(i = 1; i < m; i++){
tail = p;
p = p->next;
}
//for循环出来的时候i=m,也就是说有人报数为m了
//将结点p从链表上摘下来
tail->next = p->next;
//删除结点p之前先输出数据
if(flag == 1){
printf("%d",p->data);
flag = -1;
}else{
printf(" %d",p->data);
}
//删除结点p,即释放p所占内存
free(p);
p = tail->next;//继续使用p指向下一个编号,继续执行操作
}
//退出while循环时,p->next == p,也就是链表中只有p一个结点了
//输出最后一个人的号码
printf(" %d",p->data);
free(p);
}
}