本来是要探究多路归并的最优解的,却无意间碰到了归并排序,那就深入了解一下吧。
先来了解一下什么是 分治法。
大体思路
- 将集合看作是 n 个长度为 1 的有序子表
- 两两归并,得到 n/2 个长度为 2 的子表
叶子结点即是数据的初态,根节点为排序之后的结果。由于分治法的实现应用常常以递归的形式出现,结合递归也是操作树结构最经典方式,由此可见递归没跑了。
递归一般由两个步骤组成:
- 递进过程
- 回归过程
由于我们的排序是自下而上进行归并排序的,可以得出对原数据的切分,应该是处于递进的过程,也就是整个递归栈被构建的过程。而核心排序应该是位于调用栈里每一个函数帧在弹栈时,对上一帧后续代码的执行中。这里应该再来一张图来描述递进的过程中将每一个数据切分到叶子节点上的示例。
温馨提示:大脑中应当正确地模拟栈的整个操作,有注意理解函数的执行过程
Code Time
function mergeSort(arr, first, last) { // 构建递归结构 |
改进后的插入排序:
正常的插入排序是从数据集第二个元素开始向前方合适的位置插入,时间复杂度计算方式为阶乘,值为 O(n^2)。
这里不同的是,我们以 middle
之前的部分作为被插入的集合,之后的部分作为插入排序中无序的部分(实际上是有序的)。插入一次后,middle++
可以理解,优化的部分就在于对 first
的自增,由于我们知道后半部分实际上是有序的,因此在我们得知要插入的元素大于前方有序集合中的 某元素 时,可以推断出要插入的该元素后方的元素也同样大于前方有序的 某元素, 因此 某元素 及其之前的部分就可以从对比的范围中剔除掉了。
小谈
起初让我不解的部分是:一般回归中操作数据,都会以返回数据值的方式来与弹栈之后的栈顶函数进行通信连接,这里并没有取递归的结果,更没有返回,只是以 return
结束递进。后来发现,递进的过程中,每次都使用的是原数组,而 sortCurArr
则是直接操作原数组,调用栈里每一帧作用域中的输入数组 —— 有序子表 都是以原数组加上 first
和 last
进行标记生成的虚拟数组,因此函数调用结束后,只是将原数组的局部进行了重排。