归并排序
简述
归并排序与基于交换、选择等排序的思想不一样,“归并”的含义是将两个或两个以上的有序表组合成一个新的有序表。
算法思想
假定序列有n个记录,则可以将其看成是n个有序子序列,每个子序列的长度为1,然后两两合并,得到\(\lceil n/2 \rceil\)个长度为2或1的有序序列;再两两归并,······如此重复,直到合并成一个长度为n的有序表为止,这种方法被称为2-路归并排序。
故我们将整个算法分为两个部分,一个部分用来进行序列的归并,一个部分用来递归排序。归并的算法需要一个缓存数组,用来临时存放归并过程中的数据,归并过程中,每次从两个子序列中选择一个较小(或较大)的数放入缓存数组,需要注意的是,最后可能有部分子序列有剩余的元素,需要单独把这部分剩余元素置入缓存数组,最后再把缓存数组中的序列复制到原数组则完成归并。递归排序的算法使用两个边界数来划分递归的区间,每一趟划分都把序列划分为两个子序列,再对两个子序列分别排序,排序结束后,把两个子序列归并到一个序列中,完成一趟递归排序。
我们在这里使用了一个缓存数组,如果缓存数组在每一趟递归排序中分配,则每次递归排序的算法中需要进行缓存数组内存的分配与释放,反复多次,会造成较大的时间开销,故我们采用一次动态分配的数组作为缓存数组。
算法性能分析
- 空间效率:辅助空间需要n个单元,故空间复杂度为\(O(n)\)。
- 时间效率:每一趟归并的时间复杂度为\(O(n)\),共需要\(\lceil log_2n \rceil\)趟归并,因此时间复杂度为\(O(nlog_2n)\)。
- 稳定性:merge函数不会改变大小相同的关键字相对次序,因此是一个稳定的算法。
C++实现
#include <iostream> using namespace std; // 归并两个子序列 void merge(int a[], int b[], int low, int mid, int high) { int i = low, j = mid + 1, k = low; while (i <= mid && j <= high) { if (a[i] <= a[j]) // 选取较小的元素放入 b[k++] = a[i++]; else b[k++] = a[j++]; } // 处理剩余元素 while (i <= mid) b[k++] = a[i++]; while (j <= high) b[k++] = a[j++]; // 将缓存数组中的序列归还至原数组 for (int i = low; i <= high; ++i) a[i] = b[i]; } // 递归排序 void mSort(int a[], int b[], int low, int high) { if (low < high) { int mid = (low + high) / 2; mSort(a, b, low, mid); // 递归排序子序列 mSort(a, b, mid + 1, high); merge(a, b, low, mid, high); // 合并两个子序列 } } // 归并排序 void mergeSort(int a[], int n) { int* b = new int[n]; // 分配辅助空间内存 mSort(a, b, 0, n - 1); delete[] b; } int main() { const int SIZE = 10; int a[SIZE] = { 0 }; for (int i = 0; i < SIZE; ++i) { a[i] = rand() % 10; cout << a[i] << " "; } cout << "\n\n"; mergeSort(a, SIZE); for (auto elem : a) cout << elem << " "; return 0; }
相关推荐
清溪算法君老号 2020-06-01
seekerhit 2019-11-04
nongfusanquan0 2020-08-18
Broadview 2020-07-19
数据与算法之美 2020-07-05
Masimaro 2020-06-21
wonner 2020-06-17
Oudasheng 2020-06-04
从零开始 2020-06-05
风吹夏天 2020-05-26
Tips 2020-04-30
yangjingdong00 2020-05-11
troysps 2020-04-26
ustbfym 2020-04-25
清溪算法君老号 2020-04-24
dushine00 2020-04-19
数据与算法之美 2020-04-18
nurvnurv 2020-04-16