此为第四篇,归并排序算法。
归并排序
“归井”的含义是将两个或两个以上的有序表组合成一个新的有序表。假定待排序表含有n个记录,则可将其视为n个有序的子表,每个子表的长度为1 ,然后两两归井,得到 n/2 个长度为2或1的有序表;继续两两归并....... 如此重复,直到合并成1个长度为n的有序表为止,这种排序方法称为2路归并排序。
如下图所示为2路归并排序的一个例子,经过三趟归并后合并成了有序序列:
1.例程
/********************************* 归并排序 *********************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 功能:将前后相邻的两个有序表归并为一个有序表
// 将start ~ middle 和 middl+1 ~ end 归并成一个 start ~ end
// *temp是用来做两个有序表复制时的辅助数组
void merge(int *data, int *temp, int start, int middle, int end) {
int i = start, j = middle+1, k = start;
while (i <= middle && j <= end) { //将小的放入辅助数组temp中
if (data[i] > data[j]) {
temp[k++] = data[j++];
} else {
temp[k++] = data[i++];
}
}
while (i <= middle) { //若i有剩余则将剩余的部分直接复制到temp中
temp[k++] = data[i++];
}
while (j <= end) { //若j有剩余则将剩余的部分直接复制到temp中
temp[k++] = data[j++];
}
for (i = start;i <= end;i ++) { //把排好的temp全部复制到data
data[i] = temp[i];
}
}
// 功能:将表的前后两部分进行递归分解,然后归并
// start 和 end 可以说是为了方便递归用
int merge_sort(int *data, int *temp, int start, int end) {
int middle;
if (start < end) {
middle = start + (end - start) / 2; //从中间划分为两个子序列
merge_sort(data, temp, start, middle); //对左侧子序列进行递归排序
merge_sort(data, temp, middle+1, end); //对右侧子序列进行递归排序
merge(data, temp, start, middle, end); //归并
}
return 0;
}
int main() {
int i = 0;
int data[] = {23, 64, 24, 12, 9, 16, 53, 57};
int temp[] = {0};
int length = sizeof(data) / sizeof(int);
merge_sort(data, temp, 0, length-1);
for (i = 0;i < length;i ++) {
printf("%4d", data[i]);
}
printf("\n");
}
2.性能分析
2.1空间复杂度
在merge函数中,辅助数组temp刚好为n个单元,所以空间复杂度为:O(n)。
2.2时间复杂度
每趟归并的时间复杂度为O(n),共需进行log2n 趟归并,所以算法的时间复杂度为 O(nlog2n)。
2.3稳定性
由于merge函数在操作不会改变相同关键字记录的相对次序,所以2路归并排序算法是一种稳定的排序方法。
至此,排序算法笔记已完,下次更新其它专栏的笔记。
个人公众号:拾一札记
参考:
《王道数据结构》