分治,分而治之。先分后治
分治的一般步骤为:
因此,分治策略非常适合用递归
需要注意的是:子问题之间是相互独立的
分治的应用:快速排序、归并排序、大数乘法
分治策略通常遵守一种通用模式:
解决规模为n的问题,分解成a个规模为n/b的子问题,然后在O(n^d)时间内将子问题的解合并起来。
算法运算时间为:T(n) = aT(n/b) + O(n^d),a>0,b>1,d>=0
比如,归并排序的运行时间是:T(n) = 2T(n/2) + O(n),套用上面的图表,则T(n) = O(nlogn)
假如要排序n个数据
数据之间需要进行比较,假如两两比较,比如冒泡,需要n^2次
如果分为n/2
左边n/2两两比较,需要n/2*n/2 = n^2/4
右边也是n^2/4
左右加起来是n^2/2
再合起来的时间为O(merge)。如果合并起来的时间小于n^2/2,那么分治策略就是提升性能的。
53. 最大子序和
给定一个长度为n的整数序列,求它的最大连续子序列和
比如:-2,1,-3,4,-1,2,1,-5,4的最大连续子序列和为4 - 1 + 2 + 1 = 6
概念区分:
子序列可以不连续;
子串、子数组、子区间必须是连续的;
穷举出所有可能的连续子序列,并计算出它们的和,最后取它们中的最大值。
package divide;
public class MaxSubValue {
public static void main(String[] args)
{
int[] array = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
System.out.println(maxSubArray(array));
}
public static int maxSubArray(int[] nums) {
//边界条件
if (nums == null || nums.length == 0) return 0;
int max = nums[0];
for (int begin = 0; begin < nums.length; begin++) {
for (int end = begin; end < nums.length; end++) {
int sum = 0;
for (int i = begin; i <= end; i++) {
sum += nums[i];
}
max = Math.max(max, sum);
}
}
return max;
}
}
时间复杂度:O(n^3)
从代码可以看出,第一种暴力解法并没有利用前面已有的计算结果,而是每次都从begin加到end,其实,可以利用前面的已有结果加end即可。
public static int maxSubArray1(int[] nums) {
//边界条件
if (nums == null || nums.length == 0) return 0;
int max = nums[0];
for (int begin = 0; begin < nums.length; begin++) {
int sum = 0;
for (int end = begin; end < nums.length; end++) {
sum += nums[end];
max = Math.max(max, sum);
}
}
return max;
}
时间复杂度:O(n^2)
将序列均匀的分割成2个子序列
[begin, end) = [begin, mid) + [mid, end), mid = (begin + end) >> 1
假设[begin, end)的最大连续子序列的和是S[i,j),那么,有三种可能:
public static int maxSubArray2(int[] nums) {
//边界条件
if (nums == null || nums.length == 0) return 0;
return maxSubArray(nums, 0, nums.length);
}
public static int maxSubArray(int[] nums, int begin, int end)
{
//递归的边界条件
if (end - begin < 2) return nums[begin];
int mid = (begin + end) >> 1;
//全左最大值
int allLeftMax = maxSubArray(nums, begin, mid);
//全右最小值
int allRightMax = maxSubArray(nums, mid, end);
//一部分在左,一部分在右
int partLeftAndPartRightMax = 0;
int partRightMax = 0;
int partRightSum = 0;
for (int i = mid; i < end; i++) {
partRightSum += nums[i];
partRightMax = Math.max(partRightSum, partRightMax);
}
int partLeftMax = 0;
int partLeftSum = 0;
for (int i = mid - 1; i >= begin; i--) {
partLeftSum += nums[i];
partLeftMax = Math.max(partLeftSum, partLeftMax);
}
partLeftAndPartRightMax = partLeftMax + partRightMax;
return Math.max(partLeftAndPartRightMax, Math.max(allLeftMax, allRightMax));
}
时间复杂度分析:
T(n) = T(n/2) + T(n/2) + O(n)
可以得知,时间复杂度为O(nlogn)