介绍
归并排序在众多排序中是思路非常值得学习的一种排序,他在时间复杂度上可以保证为O(nlogn)属于稳定排序,并且他的排序利用的分治思想非常的棒的,这里我们可以先介绍一下归并排序的总体思路。
思路
归并排序的思路叫分治法,也就是说,先分解再治理。所以归并排序先采用类似二分的思想将数组逐层拆分,拆分的过程并不执行排序。等到分解到最小单元的时候再通过最小单元中的相邻元素进行比较并逐层归纳到新的数组中,最后合并到原数组。也就是说归并排序在排序过程中是需要创建临时的额外空间来处理每次的数据合并。他的思路概括就是先拆分为最小单元,最后将最小单元逐层合并为n个有序数组,然后将两个有序数组合并为新的有序数组。
图解
我们先用图解介绍一下分治思想中的拆分过程。由于思想类似于二分查找,所以我们第一步要找到数组中的中间值,因为可能有奇数偶数的分别所以我们的中间位置并不是绝对中值,所以这里我们采用Math.floor((0,arr.length-1)/2)的方式来确定中间值,本次依然拿上次的数组数据为例子
let arr = [13,2,7,21,8,65,2,0,1,9,14,7,63]
这里我们刻意的使用奇数长度的数组为例子来看一下分解的过程。请参考下图:
以上便是我们刚才所描述的根据中间值来进行二分,进一步的分解到单个元素的时候,我们将成组的单个元素再进行从下到上逐层的交换合并。
我们先将最下层的同一组内的元素进行排序交换回归到倒数第二层,然后再继续逐层向上合并如下图的效果。
上图已经形象的描述了归并排序的合并思想图解,我们参考图片就可以看见除了最底层节点交换之后,逐层向上冒泡的时候我们也是可以看见这都是两个有序数组进行排序,这样我们就可以快速的构建一个新的有序数组,直到最后一次合并的时候只剩下两个数组进行合并。
代码实现
// 定义数组
let arr = [13,2,7,21,8,65,2,0,1,9,14,7,63]
// 输出原数组
console.log(arr)
/**
* 排序的实际函数
* @param {Object} arr 目标数组
* @param {Object} left 起始位置
* @param {Object} right 终止位置
* @param {Object} temp 临时数组
*/
function sortMerge(arr,left,right,temp){
//如果起始位置比终止位置小就开启排序逻辑
if(left<right){
//确定中间值,将数组分成两部分
let mid = Math.floor((left+right)/2)
//递归执行左侧部分的分解
sortMerge(arr,left,mid,temp)
//递归执行右侧部分的分解
sortMerge(arr,mid+1,right,temp)
//分解完毕从最后一层递归中合并
//定义左部分数组比较的起点
let l = left
//定义右部分数组比较的起点
let r = mid+1
//定义临时数组的起点
let k = 0
//当左侧数组和右侧数组任意一个没有走完的时候,因为分解时可能
//左右数组长度不一样
while(l<=mid&&r<=right){
//判断哪个元素先放在数组里,将临时数组指针移动
//并对应移动比较的数组的指针
if(arr[l]>=arr[r]){
temp[k] = arr[r]
r++
k++
}else{
temp[k] = arr[l]
l++
k++
}
}
//当上面循环跳出时代表至少有一个数组走到头了
//比较左数组是否到达终点,如果没有就继续向临时数组放值
while(l<=mid){
temp[k] = arr[l]
l++
k++
}
//比较右数组是否到达终点如果没有就继续向临时数组放值
while(r<=right){
temp[k] = arr[r]
r++
k++
}
//重制临时数组指针
k = 0
//从此次归并的长度范围将临时数组的排序结果放入原数组
while(left<=right){
arr[left] = temp[k]
left++
k++
}
//输出本次的排序结果
console.log(arr)
}
}
/**
* 执行排序的函数
* @param {Object} arr 数组
*/
function sort(arr){
let left = 0
let right = arr.length-1
let temp = []
sortMerge(arr,left,right,temp)
}
sort(arr)
以上便是实际js的排序逻辑的代码实现,这里我们可以输出每次排序过程中的结果来对比图片流程更深入的理解一下。
总结
本期我们学习的是归并排序的思路,这里发现他和堆排序还有快速排序的思想都有重叠的部分,均利用了数据结构中的思想,不过实现方式不同而已,针对编程中常用的算法其实只要将图解的思路理解对于开发者来说实现只不过是看图写话的难度,所以不要将他们复杂化或者是本着背诵的方式去实现。本次分享到此就结束了,期待下次分享中与大家相遇。