理解:
- 利用递归程序不断将原数组分成左右两段,直到每一段只有一个数据时就天然有序,递归程序开始回归。
- 再利用合并程序不断将左右两边的数暂时合并到辅助数组中,较小数先放,较大数后放,相等时左边的数先放,这样就可以让辅助数组有序,并且保持稳定性。
- 最后再将辅助数组中的数放回到原数组的某一段中,就可以让原数组有序,并且保持稳定性。
代码:
//归并排序
function mergeSort(arr) {
// 不用处理的数组直接排除
if (arr == null || arr.length < 2) {
return
}
// 开始递归 递归整个数组
recursion(arr, 0, arr.length - 1)
// 数据最后都覆盖到原数组上了 可以没有返回值
}
// ------------------------3个函数-----------------------------------
// ------------------------3个函数-----------------------------------
// 递归程序 递归数组的某一段arr[l...r]
// 参数是这一段数据的左右边界
function recursion(arr, l, r) {
// 递归结束
// 只有一个数据时天然有序
if (l == r) {
return
}
// 取得某一段数据的中点 不会越界
// let m = Math.floor((l + r) / 2)
let m = l + ((r - l) >> 1)
// 递归左边一段数据 arr[l...m]
// 递归结束后 arr[l...m]已经有序
recursion(arr, l, m)
// 递归右边一段数据 arr[m+1...r]
// 递归结束后 arr[m+1...r]已经有序
recursion(arr, m + 1, r)
// 将左右数据合并到一起 arr[l...m, m+1...r]
merge(arr, l, m, r)
}
// ------------------------3个函数-----------------------------------
// ------------------------3个函数-----------------------------------
// 合并程序 合并数组某段的左右两边 arr[l...m, m+1...r]
// 参数是这一段数组的左中右边界
function merge(arr, l, m, r) {
// 开辟一个辅助数组help 长度等于这段数组的长度
const help = new Array( r - l + 1);
// 左右两边的起始指针
let p1 = l, p2 = m + 1
// 辅助数组help的起始指针
let i = 0
// 当左右两边的起始指针都没有到达各自的末尾指针时
// arr[l...p1...m, m+1...p2...r]
while (p1 <= m && p2 <= r) {
// 选择较小值暂时放入到辅助空间中
// 如果两值相等 则左值(前值)先入辅助数组help 可以保持稳定性
help[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++]
}
// 左右两边剩余的数直接进入辅助数组help
while (p1 <= m) {
help[i++] = arr[p1++]
}
while (p2 <= r) {
help[i++] = arr[p2++]
}
// 将辅助数组help中的数覆盖到原数组arr的某段中
// arrr[l......i....rrrrrrrrrrrrrrr]
// help[0......i....help.length - 1]
for (let i = 0; i < help.length; i++) {
arr[l + i] = help[i]
}
}
性能:
- 时间复杂度:O(N*log N)
- 空间复杂度:O(N) 辅助数组的最大长度
- 稳定性:可以保持。遇到相等数据时,左边数据先入辅助数组即可保持稳定性