二路归并的基本思想:将含有n个记录的原始序列看做n个长度1的子序列。从第一个子序列开始,将相邻的两个子序列合并,得到长度为2或1(最后只剩下1个子序列的情况)的子序列。如此重复成对归并,直到序列的长度等于原始序列的长度时为止。
基本过程:两个有序序列的合并。
排序过程如下图:
C++代码:
//一次二路归并过程
//对序列a[0]...a[length-1]进行一次二路归并排序,每个子序列的长度为size
void Merge(int a[], int swap[], int size, int length) {
int i, j;
int begin1, end1, begin2, end2;//分别表示两个子序列的首尾下标
int sp;//表示已经归并的元素个数
begin1 = 0;//第一个子序列的起始下标
sp = 0;
while (begin1 + size <= length - 1) { //测试是否存在两个可以合并的子序列
begin2 = begin1 + size; //第二个子序列的起始下标
end1 = begin2 - 1; //第一个子序列的结束下标
if (begin2 + size > length) {
end2 = length - 1; //第二个子序列的结束下标(第二个子序列长度不足size)
}
else {
end2 = begin2 + size - 1;
}
//下面合并两个子序列
for (i = begin1, j = begin2; i <= end1&&j <= end2;) { //注意是小于等于
if (a[i] <= a[j]) {
swap[sp++] = a[i++];
}
else {
swap[sp++] = a[j++];
}
}
//其中某一个子序列已经全部归并
while (i <= end1) {//第一个子序列还有剩余
swap[sp++] = a[i++];
}
while (j <= end2) {//第二个子序列还有剩余
swap[sp++] = a[j++];
}
begin1 = end2 + 1;//改变子序列开头以开启下一对序列的合并
}
//将原始序列中无法配对的子序列(最后落单的序列)放入结果序列中
for (i = begin1; i < length;) {
swap[sp++] = a[i++];
}
}
int * MergeSort(int a[], int length) {
int *result = new int[length];//存储归并结果
int size = 1;//记录子序列的大小
while (size < length) {//合并到子序列长度等于原始序列长度时为止
Merge(a,result,size,length); //归并的结果存于result
size = size * 2;
Merge(result, a, size, length);//归并的结果存于a
size = size * 2;
}
return a;
}
n个记录进行二次归并时,归并的次数为log_2n,每次归并过程中,记录的比较次数不超过n次。所以二次归并的时间复杂度为Ο(nlog_2n),空间复杂度为Ο(n)。二路归并排序是稳定的排序,这是它相对于快速排序的最大优点。java中,当待排序的数量大于286时,Arrays.sort()方法底层用的算法就是归并排序。
相关文章