题目:约瑟夫(Josephus)的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人按顺时针方向自1开始顺序报数,报到m时停止报数。报道m的人将出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从一报数,如此下去,直至所有的人全部出列为止。试设计一个程序求出出列顺序。
语言:C语言
要求:使用单循环链表模拟约瑟夫环
1.输入分析
本程序需要从计算机的命令行窗口输入,输入形式为
<*.exe> <人数n> <初始上限m> <密码1> <密码2> … <密码n>
用“空格”将各个数字隔开,以“回车键”作为输入结束的标志,人数n应与密码个数相同,人数n,初始上限m,密码均应为正整数,否则程序会有报错提示。
2.输出形式
假设命令行参数是齐全且正确的,输出约瑟夫环的出列顺序,并将输出结果导入文件中;
若输入参数有误,程序将有错误提示。
3.程序执行的命令包括
1)构造约瑟夫环;2)模拟约瑟夫环的执行过程;3)输出出列顺序;4)结束。
代码如下:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct LNode{
int data_1;//存储编号
int data_2;//存储密码
struct LNode *next;
}LNode, * LinkList;
LinkList InitList(){
//初始化单循环链表的头节点
LNode *node = (LNode*)malloc(sizeof(LinkList));
node->next = node;
node->data_1 = NULL;
node->data_2 = NULL;
return node;
}
void ListInsert(LinkList head,LinkList p){
//将 p 结点插入带头结点的单循环链表的末尾
LinkList temp = head->next;
while(temp->next != head){
temp = temp->next;
}
temp->next = p;
p->next = head;
return ;
}
int str_to_num(char *str){
//将字符串转换为数字
int num = 0;
int i,length;
length = strlen(str);
for(i = 0 ; i < length ; i++ ){
if(str[i] < 48 || str[i] > 57){
printf("\n数据应为正整数,请重新输入数据\n\n");
exit(0);
}
num = num * 10 + (str[i] - '0');
}
if(num <= 0){
printf("\n密码应为正整数,请重新输入数据\n\n");
exit(0);
}
return num;
}
int main(int argc,char **argv){
LinkList link_1 = InitList();//未出列的人的单循环链表的头节点,不存储实际数据
LinkList link_2 = InitList();//已出列的人的单循环链表的头节点,不存储实际数据
int n = str_to_num(argv[1]);//人数
int m = str_to_num(argv[2]);//初始最高上限
if(n != argc-3){
printf("\n人数与密码个数不匹配,请重新输入数据\n\n");
exit(0);
}
if(m <= 0){
printf("\n初始上限应为正整数,请重新输入数据\n\n");
exit(0);
}
//将输入的数据插入单循环链表link_1中
LNode *p;
int i;
for(i = 3 ; i < argc ; i++){
p = (LNode*)malloc(sizeof(LinkList));
p->data_2 = str_to_num(argv[i]);
p->data_1 = i - 2;
ListInsert(link_1,p);
}
int length_1 = argc - 3;//length_1为link_1中结点的个数
LinkList pre = link_1;
LinkList curr = link_1->next;
while(length_1){
for(i = 1 ; i < m ; i++){
pre = curr;
if(curr->next != link_1){
curr = curr->next;
}
else{
curr = curr->next->next;
}
}
//循环结束时,curr指向的结点的报数为 m ,更新m的值,在link_1中删去该节点,并插入该结点至link_2中
m = curr->data_2;
pre->next = curr->next;
ListInsert(link_2,curr);
length_1--;
if(pre->next == link_1)
curr = pre->next->next;
else
curr = pre->next;
}
//将结果读入文件并输出
FILE* fp;
char ch;
if((fp = fopen("E:\\程序设计\\数据结构\\result.txt","w")) == NULL){
printf("\n无法打开此文件result.txt\n\n");
exit(0);
}
LinkList shuchu = link_2->next;
while(shuchu != link_2){
ch = '0' + shuchu->data_1;
fputc(ch,fp);
fputc(' ',fp);
printf("%d ",shuchu->data_1);
shuchu = shuchu->next;
}
fclose(fp);
return 0;
}