You are given an array of 2n elements
The task is to interleave the array, using an in-place algorithm such that the resulting array looks like
If the in-place requirement wasn't there, we could easily create a new array and copy elements giving an O(n) time algorithm.
With the in-place requirement, a divide and conquer algorithm bumps up the algorithm to be θ(nlogn) .
So the question is:
Is there an O(n) time algorithm, which is also in-place?
Here is the answer which elaborates upon the algorithm from the paper linked by Joe:http://arxiv.org/abs/0805.1598
First let us consider a Θ(nlogn) algorithm which uses divide and conquer.
1) Divide and Conquer
We are given
a1,a2,…,b1,b2,…bnNow to use divide and conquer, for some m=θ(n) , we try to get the array
[a1,a2,…,am,b1,b2,…,bm],[am+1,…,an,bm+1,…bn]and recurse.
Notice that the portion
b1,b2,…bm,am+1,…anis a cyclic shift of
am+1,…an,b1,…bmby m places.
This is a classic and can be done in-place by three reversals and in O(n) time.
Thus the divide and conquer gives you a θ(nlogn) algorithm, with a recursion similar to T(n)=2T(n/2)+θ(n) .
2) Permutation Cycles
Now, another approach to the problem is the consider the permutation as a set of disjoint cycles.
The permutation is given by (assuming starting at 1 )
j↦2jmod2n+1If we somehow knew exactly what the cycles were, using constant extra space, we could realize the permutation by picking an element A , determine where that element goes (using the above formula), put the element in the target location into temporary space, put the element A into that target location and continue along the cycle. Once we are done with one cycle we move onto an element of the next cycle and follow that cycle and so on.
This would give us an O(n) time algorithm, but it assumes that we "somehow knew what the exact cycles were" and trying to do this book-keeping within the O(1) space limitation is what makes this problem hard.
This is where the paper uses number theory.
It can be shown that, in the case when 2n+1=3k , the elements at positions 1 , 3,32,…,3k−1 are in different cycles and every cycle contains an element at the position 3m,m≥0 .
This uses the fact that 2 is a generator of (Z/3k)∗ .
Thus when 2n+1=3k , the follow the cycle approach gives us an O(n) time algorithm, as for each cycle, we know exactly where to begin: powers of 3 (including 1 ) (those can be computed in O(1) space).
3) Final Algorithm
Now we combine the above two: Divide and Conquer + Permutation Cycles.
We do a divide and conquer, but pick m so that 2m+1 is a power of 3 and m=θ(n) .
So instead on recursing on both "halves", we recurse on only one and do θ(n) extra work.
This gives us the recurrence T(n)=T(cn)+θ(n) (for some 0<c<1 ) and thus gives us an O(n) time, O(1) space algorithm!
//轮换 void Cycle(int Data[],int Lenth,int Start) { int Cur_index,Temp1,Temp2; Cur_index=(Start*2)%(Lenth+1); Temp1=Data[Cur_index-1]; Data[Cur_index-1]=Data[Start-1]; while(Cur_index!=Start) { Temp2=Data[(Cur_index*2)%(Lenth+1)-1]; Data[(Cur_index*2)%(Lenth+1)-1]=Temp1; Temp1=Temp2; Cur_index=(Cur_index*2)%(Lenth+1); } } //数组循环移位 参考编程珠玑 void Reverse(int Data[],int Len) { int i,Temp; for(i=0;i<Len/2;i++) { Temp=Data[i]; Data[i]=Data[Len-i-1]; Data[Len-i-1]=Temp; } } void ShiftN(int Data[],int Len,int N) { Reverse(Data,Len-N); Reverse(&Data[Len-N],N); Reverse(Data,Len); } //满足Lenth=3^k-1的perfect shfulle的实现 void Perfect1(int Data[],int Lenth) { int i=1; if(Lenth==2) { i=Data[Lenth-1]; Data[Lenth-1]=Data[Lenth-2]; Data[Lenth-2]=i; return; } while(i<Lenth) { Cycle(Data,Lenth,i); i=i*3; } } //查找最接近N的3^k int LookUp(int N) { int i=3; while(i<=N+1) i*=3; if(i>3) i=i/3; return i; } void perfect(int Data[],int Lenth) { int i,startPos=0; while(startPos<Lenth) { i=LookUp(Lenth-startPos); ShiftN(&Data[startPos+(i-1)/2],(Lenth-startPos)/2,(i-1)/2); Perfect1(&Data[startPos],i-1); startPos+=(i-1); } } #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(data,n); for(i=0;i<n;i++) printf("%d ",data[i]); }
还有一个解决方案:
http://blog.youkuaiyun.com/livelylittlefish/article/details/2102457
我写的代码==
inline void swap(int &x,int &y) { int t=x; x=y; y=t; }; void reverse(int A[],int n) { int l=0,r=n-1; while(l<r) { swap(A[l],A[r]); ++l; --r; } } void rotate(int A[],int n) { if (n<=0) { return; } reverse(A,n); reverse(A+n,n); reverse(A,n+n); } void rotateMid(int A[],int n,int mid) { if (n<=0) { return; } reverse(A+mid,n); reverse(A,n+1); } void permute(int A[],int n) { if (n<=1) { return; } else if (n==2) { swap(A[1],A[2]); } else if (n==3) { vector<int> vec1(A,A+n*2); swap(A[1],A[n]); rotateMid(A+2,2,1); vector<int> vec3(A,A+n*2); } else { swap(A[1],A[n]); swap(A[n-1],A[2*n-2]); rotate(A+2,n-2); rotate(A+4,n-4); vector<int> vec(A,A+n*2); permute(A+4,n-4); } }
参考:
http://cs.stackexchange.com/questions/332/in-place-algorithm-for-interleaving-an-array