完美洗牌算法
阿新 • • 發佈:2017-12-03
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; }
完美洗牌算法