java 完美洗牌_完美洗牌算法 - 大猩猩进阶之路 - OSCHINA - 中文开源技术交流社区...

一个时间复杂度为O(n),空间复杂度为O(n)的算法:

n=4时,i<=n,位置变化(原位置->目的位置)

1->2, 2->4, 3->6, 4->8

i>n,位置变化(原位置->目的位置)

5->1, 6->3, 7->5, 8->7

任意第i个元素最终位置都为(2i)%(2n+1)。

public void LocationReplace( int a[], int n){

int n2=2*n;

int b[] = new int[n2+1];

for( int i=1; i<=n2; i++)

b[(2*i)%(n2+1)] = a[i];

for( int i=1; i<=n2; i++)

a[i]=b[i];

}

完美洗牌算法:

由上面的算法可知,n=4时,1->2->4->8->7->5->1, 3->6->3 两个圈。

i->2i mod(2n+1),[1=

void CycleLeader(int *a, int from, int mod){//mod is 2n+1

int t;

for( int i=from*2%mod; i!=from; i=i*2%mod){

t=a[i];

a[i]=a[from];

a[from]=t;

}

}

结论:若2n=3^k-1,则可确定圈的个数及各自头部的起始位置 ,且恰好有k个圈,每个圈头部位置为1,3,9,...,3^(k-1)。

分治,先找到2m=3^k-1<2n,其中m为小于n中最大的满足该式子的。

这样,1,...,m 与 n+1,...,n+m满足上面的结论,但是现实的情况是:

1,...,m,m+1,...,n,n+1,...,n+m,n+m+1,...,2n

所以这两个颜色的数应该先换位子,这样前2m个数就恰好有k个圈。

void reverse(int *a, int from, int to){

int t;

for( ; from

t=a[from];

a[from]=a[to];

a[to]=t;

}

}

void rotate(int *a,int m, int n){

reverse( a, 1, n-m);

reverse( a, n-m+1, n);

reverse( a, 1, n);

}

完美洗牌算法步骤:

输入:a[1,2,...,2n]

第1步:找到2m=3^k-1,使得3^k=<2n<3^(k+1)

第2步:把数组中的a[m+1, ...,n+m]部分循环右移m位(换位子)

第3步:前面2m长度的数组,k-1圈,每圈都执行CycleLeader方法,且mod=2*m+1

第4步:对后面的a[2m+1,...,2n]

void perfectShuffle( int *a, int n){

int n2, m, i=0, k,t;

for( ; n>1;){

//first step

n2=2*n;

for( k=0, m=1; n2/m>=3; ++k, m*=3)

;

m/=2;

//second step

rotate( a+m, m, n);

//third step

for( i=0,t=1; i

CycleLeader(a, t, m*2+1);

//fourth step

a+=2*m;

n -=m;

}

//n==1

t=a[1];

a[1]=a[2];

a[2]=t;

}

本文为《编程之法 面试和算法心得》读书笔记。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值