这是我参与更文挑战的第15天,活动详情查看: 更文挑战
分治思想简介
分治: 分而治之。将一个难以直接解决的大问题,划分成一些规模较小的子问题,以便各个击破,分而治之。
那么将分治法的概念揉入算法中来讲的话,最典型的数据结构就是数组了,将乱序的数组从中间劈开,再劈开,直至不能再小的子颗粒即长度为1。之后在长度为1的数组上进行操作,会比直接进行处理更轻松。
这个思想,老祖宗又总结了另一句话:大事化小,小事化了。
但是并非数组结构的数据都能运用,还得看是否符合一下几个条件:
该问题的规模缩小到一定的程度就可以容易地解决
该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质。
利用该问题分解出的子问题的解可以合并为该问题的解;
该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题。
经典案例
这里以排序算法中的归并(合并)算法、快速排序算法举例。同时也理一理这两个算法对于分治思想的运用。
Tips: 理解业务逻辑前,应该先清楚算法本身的逻辑;而厘清算法逻辑,需要先弄清楚他的核心思想是什么。
归并(合并)排序
归并排序,将数组从中间劈开,之后一直向下劈开,直至只剩颗粒为1的数组,之后在合并排序。
具体如下图, 上半部分是讲数据拆分的过程。,拆分为最小颗粒;下半部分进行合并,同时进行排序。
代码实现如下:
/**
* @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、不稳定,因为每次对比,都有可能不是相邻的,可能会打破相同值的顺序。
总结
分治思想及其案例就写到这,但是算法学习中,分治思想的应用并不仅限于此,还有诸如二分搜索、棋盘覆盖问题等。
不过分治思想是这一类问题的核心思想,也就是基础,重中之重。需要优先、深入学习并掌握。
磨刀不误砍柴工,能掌握、剖析核心思想,并进行相应的算法学习,最后结合业务需求进行实际使用就水到渠成。
每天进步一点点。