单循环链表解决约瑟夫环的问题

题目:约瑟夫(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;
} 
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值