一、分治法定义
在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,分治法就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。
- 分治法的精髓,也可以说是步骤:
- 分:分解——将原问题分解为规模更小的,相互独立,与原问题形式相同的子问题;
- 治:解决——若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;
- 合:合并——将各个子问题的解合并为原问题的解。
- 分治法所能解决的问题一般具有以下几个特征:
- 该问题的规模缩小到一定的程度就可以很容易地解决;
- 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质;(应用分治法的前提,此特征反映了递归思想)
- 利用该问题分解出的子问题的解可以合并为该问题的解;(关键点,若无此特征可以考虑贪心算法或动态规划)
- 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。(不包含公共的子问题,便是子问题不重复,若是重复则要考虑使用动态规划)
接下来,我们使用二分搜索来举例说明。
二、什么是二分搜索(Binary Search)?
二分搜索法,它充分利用了元素间的次序关系,采用分治策略,可在最坏的情况下用 O(log n) 完成搜索任务。
1、使用二分搜索法前提:
- 数组必须是一个有序数组,如 [1, 2, 3, 4, 5, 6, 7, 8, 9]
2、二分搜索法原理:
- 搜索时从数组中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;
- 如果中间元素大于要查找的元素,则在数组中小于中间元素的一半区间中继续查找;
- 如果中间元素小于要查找的元素,则在数组中大于中间元素的一半区间中继续查找;
- 重复第2步、第3步这个过程,直到找到这个元素,如果无剩余区间时还没有找到此元素,此元素不存在。
使用二分搜索法查找元素时,每次都能排除一半的区间,所以时间复杂度为 O(log n) 对数阶。
3、代码分析:
- 首先我们来分析一下代码中所需要的变量;因为是一个区间的范围移动,那便需要一个头,一个尾,才能确定一个区间;定义头和尾的变量为
head
和tail
;因为每次查找要缩减一半区间,需要一个中间元素为middle
且每次需要移动head
和tail
的位置来确定剩下的一半区间;最后,需要一个查找的目标元素target
; - 找到边界条件:区间为空。那什么时候区间为空呢?肯定是 head > tail 的时候;
- 重复缩减一半区间的步骤,直到找到目标元素后停止,找不到目标元素则返回 -1。
定义二分搜索方法 binarySearch:
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
function binarySearch(target){
let head = 0; // 初始化头部索引
let tail = arr.length - 1; // 初始化尾部索引
while( head <= tail){
let middle = Math.floor((head + tail) / 2); // 每次查找计算中间元素的索引
if (target === arr[middle]){
// 找到目标元素,返回目标元素的索引
return middle;
} else if (target < arr[middle]){
tail = middle - 1; // 如果目标元素小于中间元素,则移动尾部索引位置为 middle - 1
} else if (target > arr[middle]){
head = middle + 1; // 如果目标元素大于中间元素,则移动头部索引位置为 middle + 1
}
}
return -1; // 未找到目标元素,返回-1
}
let result = binarySearch(8);
console.log(result); // 7
let result2 = binarySearch(9);
console.log(result2); // 8
let result3 = binarySearch(0);
console.log(result3); // -1
还可以使用分治算法求解的一些经典问题:
扫描二维码关注公众号,回复:
17210231 查看本文章
![](/qrcode.jpg)
- 二分搜索
- 大整数乘法
- 棋盘覆盖
- 合并排序
- 快速排序
- 线性时间选择
- 最接近点对问题
- 循环赛日程表
- 汉诺塔