归并排序借鉴了分治的思想,把大的排序数组先分(logN层),后治(合并)
对于分和治来说,可以把这两个部分单独去理解
分
分就是把数组尽可能的分小,8分为4,4分为2
function sort (array, left, right) {
if (left < right) {
const mid = left + ((right - left) >> 1)
sort(array, left, mid)
sort(array, mid + 1, right)
}
}
- 在左右两个索引没有重合时,一直分下去
- 四则运算没有按位计算更快
- left + (right - left) / 2 能避免加法的溢出
分的功能就完成了
治
所谓治,就是把分到底的数组进行一个排序,因为我们最终的目的也是要排序嘛
function merge (array, left, mid, right) {
const temp = []
let i = 0
let l = left
let m = mid + 1
// 判断哪边比较大
while(l <= mid && m <= right) {
temp.push(array[l] >= array[m] ? array[m++] : array[l++])
}
// 如果左边还有剩的也加入
while(l <= mid) {
temp.push(array[l++])
}
// 如果右边还有剩的也加入
while(m <= right) {
temp.push(array[m++])
}
// 把得到的排序好的数组替换原数组
while(i < temp.length) {
array[left + i] = temp[i++]
}
}
其实排序的思路在于两个指针,对left到mid,mid+1到right两个数组之中的元素比较放入一个临时数组中
举个例子:
// [1, 3, 2, 4]
// 初始指针
let l = left = 0, m = mid = 2
// 看arr[l]大还是arr[m]大,谁大谁后放,先放的把指针向后挪一位
while(l <= mid && m <= right) {
temp.push(array[l] >= array[m] ? array[m++] : array[l++])
}
// 如果另一边的指针到最后了,但是这边还加完,那就继续加到temp里
while(l <= mid) {
temp.push(array[l++])
}
// 最后替换
顺序就是:
- 1和2比较,1先入
- 3和2比较,2先入
- 3和4比较,3先入
- 4插入
合并
把分和治的部分完成好,那就需要合并在一块
function mergeSort (array) {
function sort (array, left, right) {
if (left < right) {
const mid = left + ((right - left) >> 1)
sort(array, left, mid)
sort(array, mid + 1, right)
merge(array, left, mid, right)
}
}
function merge (array, left, mid, right) {
const temp = []
let i = 0
let l = left
let m = mid + 1
while(l <= mid && m <= right) {
temp.push(array[l] >= array[m] ? array[m++] : array[l++])
}
while(l <= mid) {
temp.push(array[l++])
}
while(m <= right) {
temp.push(array[m++])
}
while(i < temp.length) {
array[left + i] = temp[i++]
}
}
sort(array, 0, array.length - 1)
return array
}
前端的归并算法就完成啦!
归并排序的时间复杂度为O(nlogn),因为有递归,包括了所以空间复杂度为O(n)
如果有什么问题,请在下方留言