归并排序

90 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天

归并排序

思想

归并归并,先递归拆分,后逐个排序并回,干讲概念不好理解,我们直接看例子:

待排序数组:[0, 1, 4 ,2 ,3, 6, 0]

第一步,递归拆分:

第一层递归:将数组一分为二,分为[0, 1, 4, 2]和[3, 6, 0]两部分

第二层递归:继续将第一层拆分出的数组再次拆分:左半部分拆分成组合([0, 1]、[4, 2])右半部分拆分成组合([3, 6]、[0])

第三层递归:将第二层拆分出的数组继续拆分,第二层的[0, 1]拆分为组合:([0]和[1]),[4, 2]拆分为组合:([4]和[2]),右侧同理,分别为[3]和[6]以及[0]

递归拆分的工作就是把数组不停从地中间划分,最后分到每个小数组中只有一个元素为止

第二步,排序并回:

将最后递归出的组合进行每组内的排序整合

比如[0]和[1]组合,按从小到大排,比较后并为[0, 1]

[4]和[2]的组合,比较后并为[2, 4]

同理,[3] [6]并为[3, 6],[0]没有和它一组的元素,自成为一组

第三层递归的组合排序归整完毕,接下来对第二层进行操作:

现在分组为[0, 1]和[2, 4],[3, 6]和[0]

同时从一组内的两个数组头起开始扫描,两指针指向的元素进行比较,小者先进入合并数组,0小于2,0先被归整,现在左侧数组中的指针移动,指向1,右侧指针不动,指向2,1小于2,故1被归整,左侧指针无指向元素,右侧元素按序加入归整组合中,最后排序成[0, 1, 2, 4]

右侧组合也同理,0先加入归整组合中,随后是3及6,得到[0, 3, 6]

接下来就是回到了第一层递归,对[0, 1, 2, 4]和[0, 3, 6]进行整合,还是两个指针同时扫描,小者先入 -> [0, 0] -> [0, 0, 1] -> [0, 0, 1, 2] -> [0, 0, 1, 2] -> [0, 0, 1, 2, 3] -> [0, 0, 1, 2, 3, 4] -> [0, 0, 1, 2, 3, 4, 6],至此,整个归并排序结束,得到有序序列

实现(javascript)

function mergeSort(arr){
    if(arr.length < 2){
        return arr;
    }
    let mid = Math.floor(arr.length/2)
    let left = arr.slice(0, mid);
    let right = arr.slice(mid);
    //递归调用实现深层分组
    return merge(mergeSort(left), mergeSort(right));
}
​
function merge(left, right){
    let result = [];
    while(left.length && right.length){
        if(left[0] > right[0]){
            result.push(right.shift());
        }else{
            result.push(left.shift());
        }
    }
    while(left.length){
        result.push(left.shift());
    }
    while(right.length){
        result.push(right.shift());
    }
    return result;
}

时空复杂度及稳定性

平均时间复杂度:O(nlogn)

最好时间复杂度:O(n)

最差时间复杂度:O(nlogn)

空间复杂度:O(n)

稳定