从定性的角度来看,分治法的核心思想就是“分而治之”。利用分而治之的思想,就可以把一个大规模、高难度的问题,分解为若干个小规模、低难度的小问题。随后,开发者将面对多个简单的问题,并很快地找到答案各个击破。在把这些简单问题解决好之后,我们通过把这些小问题的答案合并,就得到了原问题的答案。
在大数据上分治才具有价值。问题设计的数据规则越小,它所需的计算时间越小;反之亦然。
很多高效率的算法都是以分治法作为其基础思想的,例如排序算法中的快速排序、归并排序。
分治法的使用方法
采用分治法,原问题需要具有的特征:
- 难度在降低:原问题的解觉难度,随着数据的规模的缩小而降低
- 问题可分:原问题可分解成若干个规模较小的同类问题(前提)
- 解可合并:所有子问题的解,可合并出原问题的解
- 互相独立:各个子问题之间互相独立,某个字问题的求解不会影响另一个子问题
合并法在每轮递归上,都包含了分解问题、解决问题和合并结果三个步骤。
二分查找(分治法的使用):
在一个有序的数列中,判断某个待查找的数字是否出现过
步骤:
- 选择一个标志i将集合L分为两个子集合,可以使用中位数
- 判断标志L(i)是否能与查找的值相等,相等则直接返回结果
- 如果不相等,判断L(i)与查找的值的大小
- 基于判断的结果决定下一步是向左查找还是向右查找。如果向某个方向查找的空间为0,则返回未找到
- 回到步骤1
二分查找的复杂度分析:
最差的情况,不断查找到最后一个数字,时间复杂度是O(logn)
分治法的案例
在数组 { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } 中,查找 8 是否出现过
public static void main(String[] args) {
// 需要查找的数字
int targetNumb = 8;
// 目标有序数组
int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int middle = 0;
int low = 0;
int high = arr.length - 1;
int isfind = 0;
while (low <= high) {
middle = (high + low) / 2;
if (arr[middle] == targetNumb) {
System.out.println(targetNumb + " 在数组中,下标值为: " + middle);
isfind = 1;
break;
} else if (arr[middle] > targetNumb) {
// 说明该数在low~middle之间
high = middle - 1;
} else {
// 说明该数在middle~high之间
low = middle + 1;
}
}
if (isfind == 0) {
System.out.println("数组不含 " + targetNumb);
}
}
解题思路:
- 二分查找的时间复杂度是 O(logn),这也是分治法普遍具备的特性。当你面对某个代码题,而且约束了时间复杂度是 O(logn) 或者是 O(nlogn) 时,可以想一下分治法是否可行。
- 二分查找的循环次数并不确定。一般是达到某个条件就跳出循环。因此,编码的时候,多数会采用 while 循环加 break 跳出的代码结构。
- 二分查找处理的原问题必须是有序的。因此,当你在一个有序数据环境中处理问题时,可以考虑分治法。相反,如果原问题中的数据并不是有序的,则使用分治法的可能性就会很低了。