约瑟夫环

此算法处理问题的关键是:伪链表

N个人围成一圈,从第一个人开始报数(+1),报道m的人出圈,剩下的人从1开始报数,报道m的人出圈,如此往复,直到所有人出圈。

这种问题使用链表,有些大材小用,在这里采用循环数组。


解法有两种:

一、

          这种解法的主要思想是,一开始申请一个总人数长度的数组,并使数组的初值为0;当成为厄运数时,使数组中的值为1;这就是出圈操作。而数本身的排序就是下标+1;也就是当成为厄运数时只需输出下标+1;后续操作通过数组中的值是0或1来决定是否进行报数(+1)操作;

#include <stdio.h>
#include <malloc.h>

void joseph (int count , int doom);
void joseph (int count, int doom) {
	int alive = count; //幸存人数  
	int number = 0;		//报数的数 
	int index = 0 ;		//下标 
	int *circle = NULL;
	
	circle = (int *) calloc(sizeof(int), count);
	while(alive > 0) {
		number += 1 - circle[index];//如果是已经出圈的,略过不执行报数; 
		if(number == doom) {
			printf("%d\n",index + 1);
			circle[index] = 1;
			alive--;
			number = 0;
		}
		index = (index + 1) % count;
	}
	free(circle);
	
}
int main( ) {
	int count = 11;
	int doom = 4;
	
	printf("请输入总人数,厄运数:");
	scanf("%d%d",&count,&doom); 
	joseph(count,doom);
	return 0;
}

上述方法易于理解,但是到了后面随着厄运数的增加,执行的费操作也会越来越多,时间复杂度过高。一下是第二种解法。

二、


使用上图来解释,其中index为数组下标,priIndex为前一个报数的人的下标,curIndex为当前报数的人的下标,所以上图有了最后一个指向第一个的操作。

注意:实际执行中数组中的最后一个元素是0!!!带有index字样的带表的都是右边那一列!!还有就是数组下标加一永远等于某个数一开始时的序号;

接下来做出圈操作:如果当前报数的人是厄运数字,让circle[priIndex](上一个报数的人) = circle[curIndex](当前所报数且为厄运数)的下一数,这样做保证priIndex始终指向安全的人;

这里详细解释下circle[priIndex]= circle[curIndex]比如说4是厄运数字curIndex为3时就应当执行出圈操作priIndex此时为2即为circle[2] = circle[3]=4此时改变了数组下标为2的控件的值。同时输出curIndex+1为厄运数;

这样的话在执行接下来的curIndex = circle[curIndex]时由于circle[2]=4,很自然的就跳过了3这个已经成为厄运数的数;

curIndex = circle[curIndex]即下个报数人

正常报数操作:priIndex = curIndex;

curIndex = circle[curIndex];

上式中circle[]可以理解为指向操作

下面是代码:

#include <stdio.h>
#include <malloc.h>

void joseph (int count , int doom);
void joseph (int count, int doom) {
	int alive = count; //幸存人数  
	int number = 0;		//报数的数 
	int priIndex = count - 1; // 上一个报数人; 
	int index;				 //数组下标
	int curIndex = 0; 		 //	  
	int *circle = NULL;
	
	circle = (int *) malloc(sizeof(int) *count);
	for(index = 0; index < count; index++) {
		circle[index] = (index + 1) % count;
	}
	while(alive > 0) { //只要还有幸存者就继续; 
		number = doom % alive - 1; //计算需要移动的人数;
		//直接定位到要出圈的人 
		for(index = 0;index < (number == -1? alive - 1 : number);index++) {
			priIndex = curIndex;
			curIndex = circle[curIndex];
		}
		printf("%d\n", curIndex + 1);
		alive--;
		circle[priIndex] = circle[curIndex];
		curIndex = circle[curIndex];
	}
	free(circle); 
	
}
int main( ) {
	int count;
	int doom;
	
	printf("请输入总人数,厄运数:");
	scanf("%d%d",&count,&doom); 
	joseph(count,doom);
	return 0;
}
这种方法较之第一种减少了时间复杂度,不会执行废操作;


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值