注:刚刚看到本题目更详细的分析http://blog.csdn.net/v_july_v/article/details/10212493 中间部分的“第三十五章、完美洗牌算法的变形”
上面博客中的步步前移O(n^2)算法如下(虽然这种方法效率太低,不过对后面算法的思路还是有帮助的,具体分析参考上面博客):
void swap(int& x,int& y)
{
int tmp;
tmp=x;
x=y;
y=tmp;
}
void SwapArray(int *a,int n)
{
int i,j,k;
for (i=n/2;i<n-1;i++) //i为需要交换的数的下标,实际是b[1]...b[n-1]
{
k=i;
for (j=n-i-1;j>=1;j--) //j控制循环交换的次数,具体怎么确定交换多少次,请参见上面博客中的分析
{
swap(a[k],a[k-1]);
k--;
}
}
}
题目:输入数组:{a1,a2,…,an,b1,b2,…,bn}, 在O(n)的时间,O(1)的空间将这个数组的顺序变为{a1,b1,a2,b2,a3,b3,…,an,bn}, 且不需要移动,通过交换完成,只需一个交换空间。
解答:从结果入手,结果数组的中垂线两边分别a数组的一半和b数组的一半的混合,继续将子数组以中垂线划分下去,可以看到类似的规律,因此,可以使用类似的分治算法实现。
注:其实这种方法的复杂度为O(nlgn),达不到O(n)的要求,希望会O(n)算法的童鞋能附一下答案或链接,谢谢
void swap(int *a,int i,int j)
{
int tmp;
tmp=a[i];
a[i]=a[j];
a[j]=tmp;
}
//下面以arr[10]={a1,a2,a3,a4,a5,b1,b2,b3,b4,b5}说明
void solve(int arr[],int start ,int end)
{
//初始值start=0,end=9
if( start >= end)
return;
int mid = (start+end)/2;
//lEndft part: start,...,mid;
//right part mid+1,...,end
int lStart = start;
int lEnd = mid;
int rStart = mid+1;
int rEnd = end;
//这个for循环的意思就是把需要放在左边的元素先调到左边(先不管排序情况),后面附上例子的详细解释
for(int i=(lEnd+lStart)/2+1,j = rStart ; i <= lEnd; i++,j++)
swap(arr,i,j);
//左边元素或右边元素有奇数个,为什么只判断左边元素是否为奇数呢,因为右边元素数一定等于左边元素数
//为什么是(lEnd-lStart)%2==0,比如a[0]a[1]a[2]a[3]a[4]一共5个元素,(4-0)%2==0,所以。。
//至于为什么把左右两边的元素数凑成偶数来计算,我也没看明白,但是如果没有这一步,就得不到结果,整个程序
//最重要的部分应该就是这个地方的判断了
if(lEnd!=lStart && (lEnd-lStart)%2==0)
{
lEnd++;
rStart--;
}
solve(arr,lStart,lEnd);
solve(arr,rStart,rEnd);
}
void main()
{
const int SIZE=10;
int a[SIZE]={1,3,5,7,9,2,4,6,8,10};
//int a[SIZE]={1,3,5,2,4};
for(int i=0;i<SIZE;i++)
cout<<a[i]<<" ";
cout<<endl;
solve(a,0,SIZE-1);
for(i=0;i<SIZE;i++)
cout<<a[i]<<" ";
cout<<endl;
}
下面是我写的推导过程,字迹潦草,还请见谅