3.6.2 分治
分治通常是将一个规模为n的问题划分成两个独立的子问题,其中每个子问题的规模约为n/2。大部分时候分治策略是递归形式的,并且会有简单易懂的基本条件用于结束递归。此外,在计算出两个较小问题的解之后,还必须要有一些计算来根据子问题的解计算出原问题的解。
下面来看一个例子:求包含n个数的数组中的最大元素。例3-2展示了如何将原问题分解成两个子问题并通过递归求解。通常,最大值一般是两个子集各自的最大值中比较大的那一个。仔细观察尾递归触发的条件,即子集中只有一个元素vals[left]返回。
例3-2:递归分治求数组中最大值
/* 开始使用递归函数 /
public static int maxElement(int[] vals) {
if (vals.length == 0) {
throw new NoSuchElementException("No Max Element in Empty Array.");
}
return maxElement(vals, 0, vals.length);
}
/** 计算vals[left, right)的最大元素
- 注意vals[right]并不在计算之列 */
- int maxElement(int[] vals, int left, int right) {
if (right - left == 1) {
return vals[left];
}
// 计算子问题
int mid = (left + right) / 2;
int max1 = maxElement(vals, left, mid);
int max2 = maxElement(vals, mid, right);
// 合并处理:从子问题的解中得到当前问题的解
if (max1 > max2) { return max1; }
return max2;
}
例3-2中分治算法的时间复杂度是O(n),因为子问题解的合并处理在常数时间内就能完成。如果这一步需要O(n)时间,那么整体的时间复杂度将会上升至O(n log n)。当然,我们也可以不用分治算法,直接遍历数组并且记下当前所找到的最大值。所以,就当是一个小小的提示吧:分治算法并不总是最快的。