完美洗牌

题目:有一个长度为2n的数组{a1,a2,a3,…,an,b1,b2,b3,…,b4};排序后为
{a1,b1,a2,b2,a3,b3,…,an,bn}

方法一:
让b1,b2,b3,…bn;步步前移,移到正确的位置

void solution(int *a,int n)
{
	int i,j,k,tmp;
	for(i=0;i<n;i++)
	{
		tmp = a[n+i];
		k = n-i-1; // 前移步长
		for(j = n+i; k > 0;j--,k--)
		{
			a[j] = a[j-1];
		}
		a[j] = tmp;
	}
}

方法二:每次让中间的元素交换;

void solution(int *a,int n)
{
	int left=n-1,right=n;
	for(int i=0;i<n-1;i++)
	{
		for(j=left;j<right;j+=2)
		{
			swap(a[j],a[j+1]);
		}
		--left;
		++right;
	}
}

方法3:完美洗牌
位置置换算法
在这里插入图片描述
根据初始序列与最终序列的位置对比;可以得出任意第 i 个位置都换到了 (2 i)%(2n+1)的位置;

void LocationReplace(int *a,int n)
{
	int n2 = n*2,i;
	int *b = (int*)malloc(sizeof(int)*n2);
	for(i=1;i<=n2;i++) // 下标从1开始
	{	
		b[(2*i)%(n2+1)] = a[i]; // 放到该放的地方
	}
	for(i=i;i<=n2;i++)
	{
		a[i] = b[i];
	}
	for(i = 1;i<=n2;i+=2) // 调整顺序,使得 a 在 b 的前面
	{
		swap(a[i],a[i+1]);
	}
}

走环算法
位置置换法,产生了2个圈,1->2->4->8->7->5->1, 3->6->3;

// from 为一个环的开始,mod 为 2n+1;
void CycleLeader(int *a,int from,int mod)
{
	int i;
	for(i = from * 2 % mod; i!=from; i = i*2%mod)
	{
		swap(a[i],a[from]);
	}
}

完美洗牌算法: 结论 2n = 3^k+1;
对于2n长度的数组;恰好有k个圈,且每个圈头的起始位置分别是 1,3,9,…,3^k-1;
对于给定的任意长度的 n , 可以采取分治的思想,将数组一分为2,即拆成2个部分,让一部分长度 m 满足结论,剩下的n - m 部分单独计算。
对于{ a1,a2,a3,a4,a5,a6,a7,b1,b2,b3,b4,b5,b6,b7}; k 只能取 2, n = m =4;
对于数组进行交换得到 {a1,a2,a3,a4,b1,b2,b3,b4,a5,a6,a7,b5,b6,b7};对长度m部分应用结论;再单独解决后半部分;
算法流程:
1.找到 2m = 3^k-1; 找到 k;
2.把数组中的 m+1,…,n+m 那部分循环右移m位。
3.因对于2m长度的数组,刚好有k个圈,且每个圈的头部位 3^i;其中 i =0,1,2…,k-1;
对于每个圈执行走环算法,且因数组长度为m,所以需要 对2m+1取模;
4.对数组后面部分继续使用本算法,相当于 n 减小了 m。

// 反转字符串
void reverse(int *a,int from,int to)   
{
	while(from<to)
	{
		swap(a[from++],a[to--]);
	}
}
void RightRotate(int *a,int num,int n) // 三步反转
{
	reverse(a,1,n-num);
	reverse(a,n-num+1,n);
	reverse(a,1,n);
}
// 完美洗牌算法
void PerfectShuffle(int *a,int n)
{
	int n2,m,i,k,t;
	while(n>1)
	{
		// 第一步
		n2 = n*2;
		for(k=0,m=1;(n2/m)>=3;k++,m*=3);
		m /= 2;
		// 第二步
		RightRotate(a+m,m,n);
		// 第三步
		for(i=0,t=1;k>i;i++,t*=3)
		{
			CycleLeader(a,t,m*2+1);
		}
		// 第四步
		n -= m;
		a += m*2;
	}
	// n = 1 时
	swap(a[1],a[2]);
}
void Shuffle(int *a,int n)
{
	int i,n2 = n * 2;
	PerfectShuffle(a,n);
	for(i = 1;i<=n2;i+=2) // 交换 a 和 b 的前后顺序 
	{
		swap(a[i],a[i+1]);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值