「这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战」
归并排序
介绍
归并排序 它是基于 归并操作,来进行的排序算法。采用的是 分治法 的一个思想。
大白话就是:将数据 根据某种方式 先 分 散开,再根据某种方式 进行 合 并,来得到我们想要的排序结果。
-
分开 的操作,我们使用到了,二分的方式。
-
合并 的操作,类似于数据结构中的 双指针算法 中的 快慢指针。可以看下对应的 数据结构-双指针算法 这篇文章
动图演示
步骤详解
给定数据为 const arr = [15, 5, 25, 20, 30, 10, 35]
-
分
先进行 归并排序 中 分 的操作
每次获取单个数据里面的中间下标,然后通过数组的 slice 方法,进行分割数组,之后 递归 操作,直到单个数组里的长度为 1,即可结束 递归 操作。
代码为:
// 找到中间下标 const mid = Math.floor(data.length / 2) // 分割 const left = data.slice(0, mid) const right = data.slice(mid) // 递归 return mergeSort(left), mergeSort(right)此时 分 的操作进行完毕,得到的是一排单个的数组为:
[15],[5],[25],[20],[30],[10],[35] -
合
通过上面的步骤,我们得到 分 的结果后,接下来进行 合 的操作,通过 类似与 快慢指针 一样。
该操作我们需要创建一个空的数组
const temp = []然后循环判断 left 和 right 数组是否还有值,直到其中一个数组值为空,结束循环。循环体的内容为:
-
每次取出 left 和 right 里面的第一个值进行比较
-
将小的值,先进行 shift 弹出
-
再 push 到 temp 数组中
最后进行连接操作
temp.concat(left, right),即可得到从小到大的排序数组。 -
-
这里讲解下最后一步 合 的步骤
此时可以看到
- left = [5,15,25]
- right = [10,20,30,35]
将 左指针 放置在 left 下的第 0 个下标处, 并且 右指针 放置在 right 下的第 0 个下标处。
-
第一次比较:
因为 左指针 对应的值 小于 右指针 对应的值,所以 左指针 下标进行 ++ 操作
-
第二次比较
此时 左指针 对应的值 大于 右指针 对应的值,所以 右指针 下标进行 ++ 操作
依次类推...
-
最后一次
可以看到最后一次的比较中 左指针 下标已经超出数组最大长度,也就是该 left 数组里面的值已经全部比较完毕。
所以最后将 right 剩余的值,直接连接 temp 数组中,放入即可。
完整代码
const merge = (left, right) => {
const temp = []
while (left.length && right.length) {
if (left[0] < right[0]) {
temp.push(left.shift())
} else {
temp.push(right.shift())
}
}
// 因为上面的弹出操作,所以留下了最大的值,直接将 temp 和 最大值进行连接即可
return temp.concat(left, right)
}
const mergeSort = (data) => {
if (data.length <= 1) {
return data
}
const mid = Math.floor(data.length / 2)
const left = data.slice(0, mid)
const right = data.slice(mid)
return merge(mergeSort(left), mergeSort(right))
}
总结
归并排序 是一种稳定的排序方式,且 比较次数 是所有排序中最少的。
缺点也很明显,需要开辟新的内存空间。数据量越大,就会导致此排序方式占用内存越多。