完美洗牌算法

一個時間複雜度爲O(n),空間複雜度爲O(n)的算法:java

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)spa

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];
}

 

完美洗牌算法:code

由上面的算法可知,n=4時,1->2->4->8->7->5->1, 3->6->3 兩個圈。io

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

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,...,nn+1,...,n+m,n+m+1,...,2n

因此這兩個顏色的數應該先換位子,這樣前2m個數就剛好有k個圈。

void reverse(int *a, int from, int to){
		int t;
		for( ; from<to; ++from, --to){
			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<k; ++i, t*=3)
				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;
		
	}

 

            本文爲《編程之法 面試和算法心得》讀書筆記。

相關文章
相關標籤/搜索