完美洗牌算法
题目详情:有个长度为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;
}