算法:完美洗牌算法:一个长度为2n的(整型)数组元素为 a1 a2 ... an b1 b2 ... bn

问题描述:一个长度为2n的数组元素为 a1 a2 ... an b1 b2 ... bn
要求: 用O(1)的空间代价, 在O(n)时间内把数组变成 b1 a1 b2 a2 b3 a3 ... bn an (分治法时间复杂度O(nlogn))


采取置换的方法,当需要保证似乎循环无遗漏置换。需要用到 数论原根等证明定理。

自己的算法实现:
int static findMaxNear(int length)
{
	int k=3;
	while(k-1<length) k*=3;
	if(k-1==length)
		return k-1;
	else
		return k/3 - 1;
}

void reverse(int a[],int start,int end)
{
	for(int i=start,j=end;i<j;i++,j--){
		int tmp=a[i];
		a[i]=a[j];
		a[j]=tmp;
	}

}

void rightmove(int a[],int start,int end,int steps)
{	
	reverse(a,start,end);
	reverse(a,start,start+steps-1);
	reverse(a,start+steps,end);
}


void static shuffle_3k(int a[],int start,int length)
{
	int cyclestart=1; //3^0

	while(cyclestart<=length){ //length==3^k-1,cyclestart=1,3^1,...,3^(k-1)

		int tmp=a[start+cyclestart-1],nexttmp;
		int nextpos=2*cyclestart%(length+1); //nextpos为当前元素移动到的位置
		while(nextpos!=cyclestart){ //直到回到起点
			nexttmp=a[start+nextpos-1];
			a[start+nextpos-1]=tmp;
			tmp=nexttmp; 
			nextpos=nextpos*2%(length+1);
		}	
		a[start+cyclestart-1]=tmp;

		cyclestart*=3;
	}
}



void perfect_shuffle(int a[],int start,int length)
{
	int startpos=start,end=start+length-1;

	while(startpos-start<length){
		int cur_length=end-startpos+1;
		int m=findMaxNear(cur_length);//i=3^k-1<=N
		if(m<cur_length) //还有剩余序列,则需要循环右移m/2个单位
			rightmove(a,startpos+m/2,startpos+cur_length/2+m/2-1,m/2);
		shuffle_3k(a,startpos,m);//分别以1,3,9,...,3^k为首元素开始循环置换
		startpos+=m;
	}
}


#define N 100
void main()
{
	int data[N]={0};
	int i=0;
	int n;
	printf("please input the number of data you wanna to test(should less than 100):\n");
	scanf("%d",&n);
	if(n&1)
	{
		printf("sorry,the number should be even ");
		return;
	}
	for(i=0;i<n;i++)
		data[i]=i+1;


	perfect_shuffle(data,0,n); //in perfect_shuffle 首位均改变
	for(i=0;i<n;i++)
		printf("%d   ",data[i]);


}




输入:10
即:  1 2 3 4 5 6 7 8 9 10

输出: 6 1 7 2 8 3 9 4 10 5   



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值