之前分享过一道算法叫做:寻找一个序列中的最大子序列和,如果那个算法想通了,那么归并排序开起来会显得比较简单
归并算法基本思路
将待排序序列R[0…n-1]看成是n个长度为1的有序序列,将相邻的有序表成对归并,得到n/2个长度为2的有序表;将这些有序序列再次归并,得到n/4个长度为4的有序序列;如此反复进行下去,最后得到一个长度为n的有序序列。
综上可知:
归并排序其实要做两件事:
(1)“分解”——将序列每次折半划分。
(2)“合并”——将划分后的序列段两两合并后排序。
我们先来考虑第二步,如何合并?
在每次合并过程中,都是对两个有序的序列段进行合并,然后排序。
这两个有序序列段分别为 data[low, center] 和 data[center, high]。
先将他们合并到一个局部的暂存数组p中,带合并完成后再将p复制回data中。
为了方便描述,我们称data[low, mid] 第一段,data[mid+1, high] 为第二段。
每次从两个段中取出一个记录进行关键字的比较,将较小者放入p中。最后将各段中余下的部分直接复制到p中。
经过这样的过程,p已经是一个有序的序列,再将其复制回data中,一次合并排序就完成了。
实现
合并两个升序数组
void arrayMerge(int data[], int low, int center, int high){
//创建一个临时数组作为缓冲区用来临时存储两个合并好的数组元素值
int *p = (int*)malloc(sizeof(int)*(high - low));
int i = low;
int j = center;
int k = 0;//用来记录临时数组的下标
while (i < center&&j < high){
if (data[i] < data[j]){
p[k++] = data[i++];
}
else{
p[k++] = data[j++];
}
}
//将剩余的元素存入到数组中
while(i<center){
p[k++] = data[i++];
}
while (j < high){
p[k++] = data[j++];
}
//将临时数组中的元素复制到原数组中
for (i = 0, j = low; j < high; j++){
data[j] = p[i++];
}
//释放临时数组
free(p);
}
归并排序
void mergeSort(int data[], int low, int high){
//如果当前序列中只剩下一个元素,返回
if (high-low== 1){
return;
}
int center = (high + low) / 2;
//左二分排序
mergeSort(data, low,center);
//右二分排序
mergeSort(data, center, high);
//合并两个升序数组
arrayMerge(data, low, center, high);
}
测试
int main(void){
int data[8] = { 3, 2, 5, 8, 4, 7, 6, 9 };
mergeSort(data,0,8);
for (int i = 0; i < 8; i++){
printf("%d ",data[i]);
}
system("pause");
}
调试结果: