1. 程式人生 > >完美洗牌算法

完美洗牌算法

perf namespace 希望 func 個數 時間 _each function fun

完美洗牌算法

題目詳情:有個長度為2n的數組{a1,a2,a3,...,an,b1,b2,b3,...,bn},希望排序後{a1,b1,a2,b2,....,an,bn},請考慮有無時間復雜度o(n),空間復雜度0(1)的解法。 http://blog.csdn.net/starcuan/article/details/19079913
#include <iostream>       
#include <algorithm>  
#include <functional>  
using namespace std;      
/* 
    完美洗牌算法 
    題目詳情:有個長度為2n的數組{a1,a2,a3,...,an,b1,b2,b3,...,bn}, 
    希望排序後{a1,b1,a2,b2,....,an,bn}, 
    請考慮有無時間復雜度o(n),空間復雜度0(1)的解法。 
*/  
#define N 1000  
  
// 時間O(n),空間O(n) 數組下標從1開始    
//任意的第i個元素,最終換到了 (2 * i) % (2 * n + 1)的位置  
void pefect_shuffle1(int *a,int n) {    
    int n2 = n * 2, i, b[N];    
    for (i = 1; i <= n2; ++i) {    
        b[(i * 2) % (n2 + 1)] = a[i];    
    }    
    for (i = 1; i <= n2; ++i) {    
        a[i] = b[i];    
    }    
}    
//時間O(nlogn) 空間O(1) 數組下標從1開始      
void perfect_shuffle2(int *a,int n) {      
    int t,i;      
    if (n == 1) {      
        t = a[1];      
        a[1] = a[2];      
        a[2] = t;      
        return;      
    }      
    int n2 = n * 2, n3 = n / 2;      
    if (n % 2 == 1) {  //奇數的處理      
        t = a[n];      
        for (i = n + 1; i <= n2; ++i) {      
            a[i - 1] = a[i];      
        }      
        a[n2] = t;      
        --n;      
    }      
    //到此n是偶數      
          
    for (i = n3 + 1; i <= n; ++i) {      
        t = a[i];      
        a[i] = a[i + n3];      
        a[i + n3] = t;      
    }      
          
    // [1.. n /2]      
    perfect_shuffle2(a, n3);      
    perfect_shuffle2(a + n, n3);      
}      
/* 
  
    第一個圈:1 -> 2 -> 4 -> 8 -> 7 -> 5 -> 1 
    第二個圈:3 -> 6 -> 3: 
 
    原始數組:1 2 3 4 5 6 7 8 
    數組小標:1 2 3 4 5 6 7 8 
 
    走第一圈:5 1 (3) 2 7 (6) 8 4 
    走第二圈:5 1 6 2 7 3 8 4  
*/  
/* 
神級結論:若2*n=(3^k - 1),則可確定圈的個數及各自頭部的起始位置 
對於2*n = (3^k-1)這種長度的數組,恰好只有k個圈,且每個圈頭部的起始位置分別是1,3,9,...3^(k-1)。 
*/  
//數組下標從1開始,from是圈的頭部,mod是要取模的數 mod 應該為 2 * n + 1,時間復雜度O(圈長)   
void cycle_leader(int *a,int from, int mod) {    
    int last = a[from],t,i;     
    for (i = from * 2 % mod;i != from; i = i * 2 % mod) {    
        t = a[i];    
        a[i] = last;    
        last = t;       
    }    
    a[from] = last;    
}    
//翻轉字符串時間復雜度O(to - from)    
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;    
    }    
        
}    
//循環右移num位 時間復雜度O(n)    
void right_rotate(int *a,int num,int n) {    
    reverse(a, 1, n - num);    
    reverse(a, n - num + 1,n);    
    reverse(a, 1, n);    
}    
/* 
完美洗牌算法,其算法流程為: 
    輸入數組 A[1..2 * n] 
    step 1 找到 2 * m = 3^k - 1 使得 3^k <= 2 * n < 3^(k +1) 
    step 2 把a[m + 1..n + m]那部分循環移m位 
    step 3 對每個i = 0,1,2..k - 1,3^i是個圈的頭部,做cycle_leader算法,數組長度為m,所以對2 * m + 1取模。 
    step 4 對數組的後面部分A[2 * m + 1.. 2 * n]繼續使用本算法, 這相當於n減小了m。 
*/  
//時間O(n),空間O(1)    
void perfect_shuffle3(int *a,int n) {    
    int n2, m, i, k,t;    
    for (;n > 1;) {    
        // step 1    
        n2 = n * 2;    
        for (k = 0, m = 1; n2 / m >= 3; ++k, m *= 3)  ;    
        m /= 2;    
        // 2m = 3^k - 1 , 3^k <= 2n < 3^(k + 1)    
            
        // step 2    
        right_rotate(a + m, m, n);    
            
        // step 3    
            
        for (i = 0, t = 1; i < k; ++i, t *= 3) {    
            cycle_leader(a , t, m * 2 + 1);    
                
        }    
            
        //step 4    
        a += m * 2;    
        n -= m;    
    }    
    // n = 1    
    t = a[1];    
    a[1] = a[2];    
    a[2] = t;    
}    
int main()  
{  
    int arr[]={0,1,2,3,4,5,6,7,8};  
    perfect_shuffle3(arr,4);  
    for_each(arr,arr+9,[](int i){cout<<i<<endl;});  
    return 0;  
} 

  

完美洗牌算法