问题描述:一个长度为2n的数组元素为 a1 a2 ... an b1 b2 ... bn
要求: 用O(1)的空间代价, 在O(n)时间内把数组变成 b1 a1 b2 a2 b3 a3 ... bn an (分治法时间复杂度O(nlogn))
要求: 用O(1)的空间代价, 在O(n)时间内把数组变成 b1 a1 b2 a2 b3 a3 ... bn an (分治法时间复杂度O(nlogn))
采取置换的方法,当需要保证似乎循环无遗漏置换。需要用到
数论原根等证明定理。
自己的算法实现:
int static findMaxNear(int length)
{
int k=3;
while(k-1<length) k*=3;
if(k-1==length)
return k-1;
else
return k/3 - 1;
}
void reverse(int a[],int start,int end)
{
for(int i=start,j=end;i<j;i++,j--){
int tmp=a[i];
a[i]=a[j];
a[j]=tmp;
}
}
void rightmove(int a[],int start,int end,int steps)
{
reverse(a,start,end);
reverse(a,start,start+steps-1);
reverse(a,start+steps,end);
}
void static shuffle_3k(int a[],int start,int length)
{
int cyclestart=1; //3^0
while(cyclestart<=length){ //length==3^k-1,cyclestart=1,3^1,...,3^(k-1)
int tmp=a[start+cyclestart-1],nexttmp;
int nextpos=2*cyclestart%(length+1); //nextpos为当前元素移动到的位置
while(nextpos!=cyclestart){ //直到回到起点
nexttmp=a[start+nextpos-1];
a[start+nextpos-1]=tmp;
tmp=nexttmp;
nextpos=nextpos*2%(length+1);
}
a[start+cyclestart-1]=tmp;
cyclestart*=3;
}
}
void perfect_shuffle(int a[],int start,int length)
{
int startpos=start,end=start+length-1;
while(startpos-start<length){
int cur_length=end-startpos+1;
int m=findMaxNear(cur_length);//i=3^k-1<=N
if(m<cur_length) //还有剩余序列,则需要循环右移m/2个单位
rightmove(a,startpos+m/2,startpos+cur_length/2+m/2-1,m/2);
shuffle_3k(a,startpos,m);//分别以1,3,9,...,3^k为首元素开始循环置换
startpos+=m;
}
}
#define N 100
void main()
{
int data[N]={0};
int i=0;
int n;
printf("please input the number of data you wanna to test(should less than 100):\n");
scanf("%d",&n);
if(n&1)
{
printf("sorry,the number should be even ");
return;
}
for(i=0;i<n;i++)
data[i]=i+1;
perfect_shuffle(data,0,n); //in perfect_shuffle 首位均改变
for(i=0;i<n;i++)
printf("%d ",data[i]);
}
输入:10
即: 1 2 3 4 5 6 7 8 9 10
输出: 6 1 7 2 8 3 9 4 10 5