自从看了别人的博客中的关于归并排序的O(1)空间复杂度的实现方式之后,觉得阐述的不是很明白,自己看了好久才看明白。至于具体代码和实现方式在上一篇转载的文章中已经写得蛮详细的了,又找了一篇论文看了看里面的难点——手摇法。在这里写一些自己的理解。
所谓手摇法, 即是将两个需要交换的向量各自旋转, 成一个中间向量, 然后再旋转这个中间向量即可实现两个向量的交换. 比如这两个向量我们分别表示为(FS), 我们希望把它交换为(SF), 方法是先旋转第一个向量为(FrS), 再旋转第二个向量为(FrSr), 最后旋转这个中间向量(FrSr)r 即可. 假定F 向量为(abc), S 向量为(defgh),
则初始状态为:
初始: abc defgh
旋转左半部: cba defgh
旋转右半部: cba hgfed
做整体旋转: defgh abc
结合上一篇的代码:
//实现的方法,
//待排序数据存放在array 中, 序列一下标从0 到mid-1, 序列二下标从mid 到size
void Merge( int array[],int mid,int size)
{
//first是第一部分的起始点,second是第二部的起始点,count 是计数器,表示第二部分有多少要旋转到第一部分
int first=0,second=mid,count;
while (first<second && second<size)
{
//比较两个待排序序列数据大小,如果第一部分的数值比第二部分的小,则表示不用将第二部分的数据旋转到第一部分,所以first后移
while (first<second&&array[first]<=array[second]) first++;
//count 设置初始值为0
count = 0;
//当后半部分数据较小时, 统计出连续的较小数个数count
while (second<size &&array[first]>array[second])
{
count++;
second++;
}
exchange( &array[first],count,first,second);
first+=count;
}
}
/*
这部分关键是参数意思弄明白就好了,array自然是表示待处理的数组,count表示,第二部分中有多少个元素比第一个中array[fir]小,
second表示第二部分中等待旋转的结尾数字的序号
举个例子来说,就拿1,3,5,7 2,4,6,8来说吧
在执行Merge函数的时候fir=1,即第一半到数字3停止,,second=5,即第二半到数字4停止,就是说下标为second之前的元素全小于array[fri],那么我需要旋转的部分就,
很明确了,即3,5,7,2,先旋转左半部份即为,7,5,3,在旋转右半部份,由于只有一个,所以不用动,再整体旋转:2,3,5,7这样的话这就完成了手摇法的过程
*/
/*
*/
void exchange( int *array, int count, int first,int second )
{
//m表示第一个序列未归并的的个数,exSize表示整个待旋转交换的长度
int m=second-first-count,exSize=second-first;
//手摇法
//判断连续较小数据的个数count 及序列一未归并的数据个数m, 选择使用手摇法或普通原地归并来排序
reverse( array, m );//旋转第一个序列未归并数据
reverse( array + m, exSize - m );//旋转连续较小的数据,即旋转右半部
reverse( array, exSize );//旋转整体
}
void reverse( int *array, int revSize )
{
int i = 0, rev = revSize-1,temp;
while( i < rev )
{
temp=array[i];array[i]=array[rev];array[rev]=temp;
i++;rev--;
}
}