排序算法之二分归并排序
-
上学期的数据结构已经讲过归并排序了,但是上学期太懒了,所以没有动手把代码写出来2333。今天在写算法设计的第二章作业,讲的是分治思想。而归并排序的思想就是分治,便乘此机会动手实际写一下归并排序。
-
大学入学以来好吃懒做,算法方面根本没有什么进步,排序还只会高中学的冒泡排序和选择排序…在之后的学习生活中,一定要重视算法,动手实操并真正理解算法。
-
以下是我写的二分归并排序的代码
#include <iostream> #include <cstdlib> #include <ctime> using namespace std; int a[11] = {0}; int b[11] = {0}; void Merge(int a[], int low, int mid, int high) { int i = low, j = mid + 1, k = low; //分别代表需要归并的的左部分的初始坐标i,右部分的初始坐标j,以及辅助数组的初始坐标k while (i <= mid && j <= high) { if (a[i] <= a[j]) //左半部分的值比较小,存入辅助数组之中 b[k++] = a[i++]; else b[k++] = a[j++]; } while (i <= mid) //将a中左部分和右部分的剩余元素依次存入辅助数组之中,当然这个和接下来的while循环很显然只能运行一个,因为左部分和右部分当中的一个已经全部加入辅助数组中了,不然是跳不出上一个while循环的 b[k++] = a[i++]; while (j <= high) b[k++] = a[j++]; for (int index = low; index <= high; index++) a[index] = b[index]; //用辅助数组已经有序的值,把原始数组进行覆盖,最后的a数组就是merge之后有序的了 } void MergeSort(int a[], int low, int high) { if (low < high) //很重要,避免出现low = high导致死循环局面的发生,实际上也是往深层次递归的终止条件 { int mid = (low + high) >> 1; MergeSort(a, low, mid); //分治的思想,划分子问题 MergeSort(a, mid + 1, high); Merge(a, low, mid, high); //将之前划分出来的两个子问题进行合并,得到最终有序的序列 } return; } int main() { srand(time(0)); for (int i = 0; i < 11; i++) //产生随机数 { a[i] = rand() % 20; cout << a[i] << " "; } cout << endl; MergeSort(a, 0, 10); for (int i = 0; i < 11; i++) cout << a[i] << " "; cout << endl; return 0; }
-
其中的MergeSort函数会把问题规模从原来的(low,high)分成两个部分,分别是(low,mid)和(mid+1,high),进行递归,而子问题也会继续向下递归,直到low == high也就是某个部分中只有一个元素了。
-
以下是我画的递归图
-
最后画出来就像一棵树,我们进行的操作仿佛就是树的后序遍历(也就是最后访问根节点)。我们可以把左子问题理解为左子树,右子问题理解为右子树,最后的把两个子问题进行归并的操作理解为访问根节点。
-
这里我举一个例子方便理解。MS(a,0,10)会一直向深层次递归,经过MS(a,0,5),接着为MS(a,0,2),然后为MS(a,0,1),最后是MS(a,0,0),然后因为这时候low == high,所以我们会直接renturn(程序28行和35行)。然后到达了MS(a,1,1,),同样return,相当于已经访问完毕了MS(a,0,1)的左子树和右子树,接下来我们需要把这两个子问题进行归并,理解为访问根节点。
-
这里我还想要说一点,左右子问题的规模可能是不一样的,比如MS(a,0,2)这个问题中,左子问题的规模为2,而右子问题的规模是1,但是由于我们Merge归并函数写的好,所以可以正常归并2333
-
实际运行结果
-
参考链接