排序4:普通归并排序
来源:互联网 发布:七匹狼皮带怎么样 知乎 编辑:程序博客网 时间:2024/05/22 11:40
归并排序,把数据分词若干部分,每个部分再分为若干部分,这样逐渐直到每个小部分满足预定的可进行排序的数量时,再进行各自的排序;继而每个排序后的小部分再按拆分时的顺序进行再排序的合并,最终合并为之前的整体。
上面的描述非常涩,下面画图来描述:
比如原始数据是[4,3,2,6,5,7,1],那么首先规定我们的预定是:
1、分2路进行归并排序(典型的归并排序是2路归并,实际还包括多路归并排序);
2、当拆分的小数据集的长度小于等于2的时候,不用再拆分了,进入合并排序
那么就根据上面的预定,下面开始归并排序:
1、首先将原始数据集分为2部分,即[4,3,2,6]和[5,7,1],显然长度分别为4和3
2、显然不满足"拆分的小数据集的长度小于等于2的时候"的停止拆分条件,继续拆分,[4,3,2,6]拆分为[4,3]和[2,6],[5,7,1]拆分为[5,7]和[1]
3、[4,3]和[2,6],长度均为2,满足停止拆分条件,进入合并排序,合并排序后结果为[2,3,4,6]
4、[5,7]和[1],长度分别为2和1,满足停止拆分条件,进入合并排序,合并排序后结果为[1,5,7]
5、[2,3,4,6]和[1,5,7]再做合并排序,结果为最终结果为[1,2,3,4,5,6,7]
看上图,其实和快速排序有一个相似的地方就是,都是把数据集不断分成一些区间,但本质上不同的是:
1、快速排序是基于标杆数据和其他数据的比较,划分区间的分治
2、典型的归并排序是直接划分区间,然后再对区间内数据进行排序
归并排序的核心,了解了这些就对归并排序的原理有了较为透彻的理解:
1、两个数据集N1和N2的合并排序:时间复杂度是O[N1 + N2],比如[4,3]和[2,6]的合并排序为[2,3,4,6],时间复杂度是O(4),因为使用了O(4)的空间复杂度,方式是不断找最小的树放入一个临时空间,所以能在O(4)的时间复杂度下完成合并排序。
2、归并排序的时间复杂度:最好最坏平均都是O(N * logN),为什么?看上图的merge部分,每一次merge的时间复杂度都是O(N),进行了多少次merge?很明显,2次,二叉树原理,看原始数据需要多少次二分达到停止继续拆分条件,后面就需要多少次的merge操作,即所需的时间复杂度具体是:O(N * (logN - 1)) -> O(N * logN)。而且归并排序不受原始数据影响,都是
3、归并排序是稳定的:观察上面的图,归并排序根本没有交换排序、插入排序的那些可能出现的颠倒位置的情况。这个是相比于快速排序和堆排序的一个重大优势,保证了同值数据不会排序后顺序变化。
4、归并排序使用了空间复杂度:之前的交换、选择、插入排序都还没有使用额外空间助力排序,而归并排序在合并排序时是通过临时空间达到O(N)的线性时间复杂度。这在数据量较大时是个缺陷。
5、让归并排序的空间复杂度降低为不需要空间复杂度:方法就是把合并排序里改改,往往是改成用快排代替掉,但这样并不太好,虽然空间复杂度下降,但最终归并排序的平均时间复杂度变为了O(N * logN * logN),还不如直接去快排。
6、归并排序更大的改进空间在于:并行化、多路归并排序
7、归并排序应用场景:归并排序需要额外空间的这个特点,往往在基于内存的内部排序中较少用到,最多用于外部排序
代码及注释:
merge.h(类声明):
#include <vector>template<class T> class mergesort {std::vector<T> data;void msplit(int start, int end);void merge(int start1, int end1, int start2, int end2);public:mergesort(T *_data, int size);mergesort(std::vector<T> _data);~mergesort(){data.clear();}void msort();void show(bool direct);};merge_func.h(类实现):
#include "merge.h"#include <iostream>template<class T> mergesort<T>::mergesort (T *_data, int size) {for (int i = 0; i < size; i++) {data.push_back(_data[i]);}msort();}template<class T> mergesort<T>::mergesort (std::vector<T> _data) {data = _data;msort();}//归并排序的合并排序部分, 达到O(N)的排序时间复杂度, 是因为使用了O(N)的空间复杂度template<class T> void mergesort<T>::merge (int start1, int end1, int start2, int end2) {std::cout << "merge: " << start1 << ", " << end1 << "; " << start2 << ", " << end2 << std::endl;int baseidx = start1;int size = end2 - start1 + 1;int *tmp = new int[end2 - start1 + 1];int idx = 0;while (start1 <= end1 && start2 <= end2) {if (data[start1] <= data[start2]) {tmp[idx] = data[start1];++start1;++idx;continue;}if (data[start2] < data[start1]) {tmp[idx] = data[start2];++start2;++idx;continue;}}while (start1 <= end1) {tmp[idx] = data[start1];++idx;++start1;}while (start2 <= end2) {tmp[idx] = data[start2];++idx;++start2;}for (int i = 0; i < size; i++) {data[baseidx + i] = tmp[i];}delete []tmp;}//传统的2路归并排序. 整个数据集分为[start1, end1]和[start2, end2]两路各自去做归并排序, //而[start1, end1]和[start2, end2]两个子数据集, 也会继续不断的拆分2路再去做归并排序//直到(end - start <= 1), 往往的实现中是(end >= start), 我这样做是减少了无谓的递归到头, start1 == end1及start2 == end2, merge时只是比较两个数, 过多的递归//其实并没有减少时间复杂度, 因为merge函数的平均时间复杂度就是O(N), 只是喂给merge的输入次数减少, 输入数据尽可能杜绝了单个数template<class T> void mergesort<T>::msplit (int start, int end) {if (end - start <= 1) {return;}//不断的二叉拆分, 直到数据集长度小于等于2时, 停止继续拆分, 进入mergeint start1 = start, end1 = (start + end)/2;int start2 = end1 + 1, end2 = end;std::cout << "msplit1: " << start1 << ", " << end1 << std::endl;msplit(start1, end1);std::cout << "msplit2: " << start2 << ", " << end2 << std::endl;msplit(start2, end2);merge(start1, end1, start2, end2);}template<class T> void mergesort<T>::msort () {msplit(0, data.size() - 1);}template<class T> void mergesort<T>::show (bool direct) {if (direct) {for (int i = 0; i < data.size(); i++) {std::cout << data[i];if (i != data.size() - 1) {std::cout << ", ";}}} else {for (int i = data.size() - 1; i >= 0; i--) {std::cout << data[i];if (i != 0) {std::cout << ", ";}}}std::cout << std::endl;}merge.cpp(测试程序):
#include "merge_func.h"#include <stdlib.h>int main () {int *testdata = new int[sizeof(int) * 9];srand((int)time(0));for (int i = 0; i < 9; i++) {testdata[i] = rand() % 1000;std::cout << testdata[i] << ", ";}std::cout << std::endl;//int testdata[] = {97, 87, 72, 86, 69, 75, 22, 1, 53, 54, 58, 86, 74, 51, 60, 17, 14, 35, 91, 27, 74, 85, 39, 66, 24, 59, 45, 16, 30, 92};mergesort<int> mergesorter(testdata, 9);mergesorter.show(1);delete []testdata;return 0;}
- 排序4:普通归并排序
- 排序4-归并排序
- 【排序4】归并排序
- 排序4:归并排序
- C语言实现普通归并排序
- 归并排序-归并排序
- 内排序(4):归并排序
- 归并排序
- 归并排序
- 归并排序
- 归并排序
- 归并排序
- 归并排序
- 排序::归并
- 归并排序
- 归并排序
- 归并排序
- 归并排序
- 字符编码
- Service服务
- Matlab数据的可视化 -- 极坐标图及其与直角坐标图的转换
- 2017.2.12 HTML/CSS有感2
- 初识Angular
- 排序4:普通归并排序
- Android:Binder机制原理
- Python自学 第一次作业
- CentOs7 LAMP Drupal安装记录
- K-Means聚类算法原理
- tp5简单的多图片上传并预览demo
- Android 四大组件详解
- C和C++运行库
- 用scikit-learn学习K-Means聚类