分治思想:归并排序与快速排序

606 阅读3分钟

这是我参与更文挑战的第15天,活动详情查看: 更文挑战

分治思想简介

分治: 分而治之。将一个难以直接解决的大问题,划分成一些规模较小的子问题,以便各个击破,分而治之。

那么将分治法的概念揉入算法中来讲的话,最典型的数据结构就是数组了,将乱序的数组从中间劈开,再劈开,直至不能再小的子颗粒即长度为1。之后在长度为1的数组上进行操作,会比直接进行处理更轻松。

这个思想,老祖宗又总结了另一句话:大事化小,小事化了。

但是并非数组结构的数据都能运用,还得看是否符合一下几个条件:

  1. 该问题的规模缩小到一定的程度就可以容易地解决

  2. 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。

  3. 利用该问题分解出的子问题的解可以合并为该问题的解;

  4. 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。

经典案例

这里以排序算法中的归并(合并)算法、快速排序算法举例。同时也理一理这两个算法对于分治思想的运用。

Tips: 理解业务逻辑前,应该先清楚算法本身的逻辑;而厘清算法逻辑,需要先弄清楚他的核心思想是什么。

归并(合并)排序

归并排序,将数组从中间劈开,之后一直向下劈开,直至只剩颗粒为1的数组,之后在合并排序。

具体如下图, 上半部分是讲数据拆分的过程。,拆分为最小颗粒;下半部分进行合并,同时进行排序。

image.png

代码实现如下:

/**
 * @param {Array} arr 需要排序的数组
 * return {Array} 排序之后的数组
 */
function mergeSort(arr){
    let len - arr.length,
        middle = Math.floor(len / 2),
        left = [], right = [];
    // 长度小于2 即只有0或1位,直接返回,真实业务中需要考虑类型、值是否可排序等情况。
    if(len < 2) return arr;
    // 将数组拆分为左右两半。
    left = arr.slice(0, middle);
    right = arr.slice(middle);
    
    return merge(left, right)
}
/**
 * @param {Array} left 拆分后的左侧数组
 * @param {Array} right 拆分后的右侧数组
 * return result 合并后的结果数组
 */
function merge(left, right){
    const result = [];
    while(left.length && right.length){
        if(left[0] <= right[0]) result.push(left.shift())
        else result.push(right.shift())
    }
    
    while(left.length) result.push(left.shift())
    while(right.length) result.push(right.shift())
    
    return result;
}

mergeSort([1,5,6,2,4,3]) // [1,2,3,4,5,6]

优势:

1、速度快:时间复杂度 O(nlogn)

2、稳定:如果遇到[3,2,1,2]这样的数组,排序后两个2的先后顺序不会变,特定场景下这很重要

劣势:

需要的空间复杂度相对较高

快速排序

快速排序的核心也是分治,所以他在实现的过程中也是拆分数组,合并数组。

但是不同于归并排序的是,快速排序会确定一个基准值,将之后的值与基准值做比对,小于放左边,大于放右边。

/**
 * @param {Array} arr 需要排序的数组
 * return {Array} 排序完成的结果数组
 */
function quickSort(arr){
    if(arr.length <= 1) return arr;
    
    let len = arr.length,
        left = [], right = [];
    
    const middleVar = arr.splice(Math.floor(len / 2), 1)[0];
    
    for(let i = 0; i < arr.length; i++){
        if(arr[i] < middleVar) left.push(arr[i]);
        else right.push(arr[i]);
    }
    
    return quickSort(left).concat(middleVar, quickSort(right));
}

优势:

1、速度快:时间复杂度 O(nlogn)

劣势:

1、需要的空间复杂度相对较高

2、不稳定,因为每次对比,都有可能不是相邻的,可能会打破相同值的顺序。

总结

分治思想及其案例就写到这,但是算法学习中,分治思想的应用并不仅限于此,还有诸如二分搜索、棋盘覆盖问题等。

不过分治思想是这一类问题的核心思想,也就是基础,重中之重。需要优先、深入学习并掌握。

磨刀不误砍柴工,能掌握、剖析核心思想,并进行相应的算法学习,最后结合业务需求进行实际使用就水到渠成。

每天进步一点点。