归并排序采用了分治策略,在对数组进行排序的时候,先将数组分割成更小的子数组,然后对子数组排序,最后排序后的子数组合并成一个完成的数组,在对子数组排序的时候可以使用相同的策略继续对子数组进行分割,直到子数组中只包含一个元素。
例如我们需要数组[3,2,6,4,8,1,7,5]排序,首先将数组分割成只包含单个元素的子数组:

[3] [2]  [6] [4]  [8]  [1]  [7]  [5]

将相邻的两个子数组合并,得到包含两个元素的已排序子数组:

[2, 3]   [4, 6]  [1, 8]  [5, 7]

继续两两合并:

[2, 3, 4, 6]  [1, 5, 7, 8]

最终就可以得到一个完成的排序好的数组了:

[1, 2, 3, 4, 5, 6, 7, 8]

下面看一下怎么用JS代码实现一个归并排序。
首先看一下怎么合并两个已排序的子数组,代码如下:

/**
 * 辅助方法,合并数组arr中的两个已排序的子数组
 * 作为例子只考虑从小到大排序,其他排序可自定义比较函数,类似于Array#sort
 * @param {*} arr 数组
 * @param {*} start 起始位置
 * @param {*} end 结束位置(包含在范围内)
 * @param {*} mid 两个子数组的分割位置
 */
function merge(arr, start, end, mid) {
 // 需要合并的元素个数
 const count = end - start + 1;
 // 左侧已排序数组
 const left = arr.slice(start, mid + 1);
 // 右侧已排序数组
 const right = arr.slice(mid + 1, end + 1);
 // 加入守卫元素,这样可以不用判断子数组是否为空
 left.push(Infinity);
 right.push(Infinity);
 // 依次取出两个子数组中的较小的值
 let i = 0;
 let l = 0;
 let r = 0;
 while(i < count) {
 if (left[l] < right[r]) {
 arr[start + i] = left[l];
 l++;
 } else {
 arr[start + i] = right[r];
 r++;
 }
 i++;
 }
}

可以把两个已排序的子数组看作是两堆扑克,每次从两队扑克中取出较小的一张牌,直到所有的牌取完,得到的就是一副排好序的扑克。

下面看一下如何使用递归实现归并排序:
image.png
首先计算出中心点将数组分割成两个子数组,对两个子数组分别排序,最后调用merge函数将两个已排序的子数组合并得到最终的排序数组。


helloweilei
130 声望2 粉丝

菜鸟一枚,各位道友多多指教!