约瑟夫环

 

【问题描述】

约瑟夫(Joseph)问题的一种描述是:编号为12,…,nn个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序求出出列顺序。

【基本要求】

利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。

【测试数据】

m的初值为20n=77个人的密码依次为:3172484,首先m值为6(正确的出列顺序应为6147235)。


#include<stdio.h>
#include<stdlib.h>
typedef struct node{
	int data;/*存储密码*/
	int num;/*存储序列号*/
	struct node *next;
}node,*Linklist;
/*创建存有密码、序列号的链表*/
Linklist creatlist(int n,int *a){
	Linklist head;
	Linklist p,q;
	int i=1;
	if((head=(Linklist)malloc(sizeof(node)))==NULL){
		printf("分配内存失败");
		return NULL;
	}
	head->data=a[i-1];
	head->num=i;
	head->next=head;
	q=head;
	i++;
	while(i<=n){
		if((p=(Linklist)malloc(sizeof(node)))==NULL){
			printf("分配内存失败");
			return NULL;
		}
		p->data=a[i-1];
		p->num=i;
		q->next=p;
		q=q->next;
		q->next=head;
		i++;
	}
	return head;
}
	
/*采用递归算法找到出列人*/
void findnode(Linklist head,int m,int n){
	Linklist q;
	int i;
	if(n!=0){
		if(m>1){
			q=head->next;
			for(i=1;i<m-1;i++){
				head=head->next;
				q=q->next;
			}
			head->next=q->next;
			head=head->next;
			m=q->data;
			printf("%d\t",q->num);
			free(q);
			findnode(head,m,n-1);
		}
		else{ 
			q=head;
			head=head->next;
			printf("%d\t",q->num);
			free(q);
			findnode(head,m,n-1);
		}
	}
}

int main(){
	int n;
	printf("请输入人数:\n");
	scanf("%d",&n);
	int a[n];
	printf("请输入每个人持有的密码:\n");
	int i;
	for(i=0;i<n;i++){
		scanf("%d",&a[i]);
	}
	Linklist head;
	head=creatlist(n,a);
	int m;
	printf("请输入密码的上限值:\n");
	scanf("%d",&m);
	printf("出列序号:\n");
	findnode(head,m,n);
	return 0;
}



运行结果:



总结:

        1、对链表还不是太熟悉,尤其是这种循环链表,有些东西还得仔细想,比如44-47行:

for(i=1;i<m-1;i++){
     head=head->next;
     q=q->next;
}
一开始不知道i<m还是i<m-1,在运行了代码后才知道应该i<m-1。

       2、对各种情况考虑不够完善,总是会少考虑一或几种情况,比如在findnode函数中,当这个密码m=1和m>1时是完全不一样的,少考虑了m=1的情况,输出结果就会出现这种情况:

6     1     4      7      2     5       3

因为当m=1时,i<m-1=0显然是不成立的,不信可以将m=1时的情况删去,运行一下看看结果,是不是很好玩啊。

        3、其实最近也有关注算法的效率问题,在findnode函数中,当这个密码m的值越来越大,它做的无用功也越来越多,为了提高它的效率,我的办法是  m%n 既取余,当然在实际中是不能这样的,因为每个人都要报数,但是这种方法我没有试(真实情况是我比较懒),有心人可以做做看

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值