标签:style 思想 time rgb str padding nts 计算机 mst
1945年,约翰·冯·诺依曼(John von Neumann)发明了归并排序,这是典型的分治算法的应用。在计算机科学中,归并排序是一种高效、通用、基于比较的排序算法。此外,归并排序还是稳定的,因为相同元素的相对次序在排序后不会发生变化。最开始,归并排序采用的是自顶向下的模式,后来,到了1948年,冯大神和赫尔曼·海因·戈德斯坦(Herman Heine Goldstine)两人共同撰写了一篇报告,描述了归并排序的另外一种实现方式,那就是自底向上。
接下来,本文将带你逐一讨论归并排序的这两种实现方式。
膜拜一下两位大佬(〃‘▽‘〃)。
归并排序的思想很简单:
这里面就用到了非常重要的分治思想,把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,最后将子问题的解合并,就得到了原问题的解。
以数组A[8] = {6, 3, 2, 7, 1, 5, 4, 8}
为例,归并排序自顶向下的排序过程如下图所示:
在计算机程序中,这一过程可以用递归来实现。以下是C++代码实现:
1/*自顶向下,递归
2 Array:待排序的数组首地址
3 low:待排序的范围的下界
4 high:待排序的范围的上界的后一个位置
5 比如你要对数组Array[0]~Array[5]进行排序,那么low=0,high=6*/
6void mergeSort_Recursive(int* Array, int low, int high) {
7 if (low + 1 < high) { //当子数组的长度大于1时,不断对数组进行分解
8 int mid = low + (high - low + 1) / 2; //将数组分解成Array[low, mid)和A[mid, high),圆括号表示开区间,即数组中不包含此元素
9 mergeSort_Recursive(Array, low, mid);
10 mergeSort_Recursive(Array, mid, high);
11 merge(Array, low, mid, high); /*合并Array[low, mid)和A[mid, high),该函数在文末的完整代码中给出*/
12 }
13}
自底向上的思想就是,进行分解,直接从线性表中的单个元素开始,进行两两合并,然后再以每两个元素为单位,进行两两合并……直到最后只剩下一个线性表。还是以刚才那个数组为例,自底向上的归并排序图示如下:
很明显,这种方式跳过了分解的步骤,操作步骤少了很多,而且在用代码实现时,采用的是迭代而不是递归的方式,空间复杂度也少了很多。
下面是C++代码实现:
1/*自底向上,迭代
2 Array:待排序的数组首地址
3 low:待排序的范围的下界
4 high:待排序的范围的上界的后一个位置
5 比如你要对数组Array[0]~Array[5]进行排序,那么low=0,high=6*/
6void mergeSort(int* Array, int low, int high)
7{
8 int step = 1;
9 while (step < high) {
10 for (int i = low; i < high; i += step << 1) {
11 int lo = i, hi = (i + (step << 1)) <= high ? (i + (step << 1)) : high; //定义二路归并的上界与下界
12 int mid = i + step <= high ? (i + step) : high;
13 merge(Array, lo, mid, hi);
14 }
15
16 //将i和i+step这两个有序序列进行合并
17 //序列长度为step
18 //当i以后的长度小于或者等于step时,退出
19 step <<= 1;//在按某一步长归并序列之后,步长加倍
20 }
21}
在最坏情况下,以上两种方式的时间复杂度均为。采用自顶向下的归并排序要用到递归,这种方法的好处就是代码容易理解,能比较直观地描述算法思想。但是,也正是由于递归,会使得在排序过程中占用大量计算机内部的栈空间,如果线性表长度过长,那么会可能会造成栈溢出,从而使程序崩溃,而自底向上的迭代就不会出现这种问题。所以,综合来看,在实际应用中,建议大家采用自底上的归并排序——也就是迭代。
递归对性能的消耗有时候甚至能达到指数级别,事实上,任何能用递归解决的问题,也都能用迭代解决。所以,不仅是归并排序,当你遇到其他问题时,如果能用迭代解决,那么不妨想想能不能用迭代来替换。
以下是完整代码:
1#include <iostream>
2
3using namespace std;
4
5/*合并Array[low, mid)和Array[mid, high)
6 合并前应保证Array[low, mid)和Array[mid, high)中的元素都是有序的*/
7void merge(int* Array, int low, int mid, int high){
8 int* A = Array + low; //合并后的向量A[0, high - low) = _elem[low, high)
9 int lb = mid - low;
10 int* B = new int[lb]; //前子向量B[0, lb) = _elem[low, mid)
11 for (int i = 0; i < lb; B[i] = A[i++]); //复制前子向量B
12 int lc = high - mid;
13 int* C = Array + mid; //后子向量C[0, lc) = _elem[mid, high)
14
15 /*i, j, k分别指向A, B, C中的元素*/
16 for (int i = 0, j = 0, k = 0; (j < lb) || (k < lc);) { //B[j]和C[k]中小者转至A的末尾
17 if (j < lb && k < lc) //如果j和k都没有越界,那么就选择B[j]和C[k]中的较小者放入A[i]
18 A[i++] = B[j] < C[k] ? B[j++] : C[k++];
19 if (j < lb && lc <= k) //如果j没有越界而k越界了,那么就将B[j]放入A[i]
20 A[i++] = B[j++];
21 if (lb <= j && k < lc) //如果k没有越界而j越界了,那么就将C[k]放入A[i]
22 A[i++] = C[k++];
23 }
24
25 delete[] B; //释放临时空间B
26}
27
28/*自底向上,迭代
29 Array:待排序的数组首地址
30 low:待排序的范围的下界
31 high:待排序的范围的上界的后一个位置
32 比如你要对数组Array[0]~Array[5]进行排序,那么low=0,high=6*/
33void mergeSort(int* Array, int low, int high)
34{
35 int step = 1;
36 while (step < high) {
37 for (int i = low; i < high; i += step << 1) {
38 int lo = i, hi = (i + (step << 1)) <= high ? (i + (step << 1)) : high; //定义二路归并的上界与下界
39 int mid = i + step <= high ? (i + step) : high;
40 merge(Array, lo, mid, hi);
41 }
42
43 //将i和i+step这两个有序序列进行合并
44 //序列长度为step
45 //当i以后的长度小于或者等于step时,退出
46 step <<= 1;//在按某一步长归并序列之后,步长加倍
47 }
48}
49
50/*自顶向下,递归
51 Array:待排序的数组首地址
52 low:待排序的范围的下界
53 high:待排序的范围的上界的后一个位置
54 比如你要对数组Array[0]~Array[5]进行排序,那么low=0,high=6*/
55void mergeSort_Recursive(int* Array, int low, int high) {
56 if (low + 1 < high) { //当子数组的长度大于1时,不断对数组进行分解
57 int mid = low + (high - low + 1) / 2; //将数组分解成Array[low, mid)和A[mid, high),圆括号表示开区间,即数组中不包含此元素
58 mergeSort_Recursive(Array, low, mid);
59 mergeSort_Recursive(Array, mid, high);
60 merge(Array, low, mid, high); //合并Array[low, mid)和A[mid, high)
61 }
62}
63
64int main() {
65 int A[8] = { 6, 3, 2, 7, 1, 5, 8, 4 };
66
67 mergeSort(A, 0, 8);
68 //mergeSort_Recursive(A, 0, 8);
69 for (int i = 0; i < 8; i++) {
70 cout << A[i] << " ";
71 }
72 system("pause");
73 return 0;
74}
最后,欢迎大家关注我的微信公众号:AProgrammer
注:文中部分图片来自网络
标签:style 思想 time rgb str padding nts 计算机 mst
原文地址:https://www.cnblogs.com/AProgrammer/p/10005876.html