题目:输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。
示例:
输入: nums = [1, -2, 3, 10, -4, 7, 2, -5]
输出: 18
解释: 连续子数组 [3, 10, -4, 7, 2] 的和最大,为 18。
题目分析:
经过一段时间的探索,这个题目除动态规划以及累加法【面试题42-连续子数组的最大和(一)】,这题也可以使用分治算法进行求解。
总体思想:将数组一分为二,我们在左边部分寻找一个最大的连续子数组和a,在右边部分寻找一个最大的连续子数组和b。也可能最大的连续子数组和处于中间阶段。即跨中间分界线而存在的一个子数组。如果是跨中间得话,一定包含左部分的最后一个元素,右部分的第一个元素(图中标红部分),所以为了寻找最大值,我们需要往左和右扩展,直到找到最大值c。整个数组的最大连续子数组,就是max(a,b,c)。如下图所示:
Java代码:
public class Offer42 {
public static void main(String[] args) {
int[] array1 = {1, -2, 3, 10, -4, 7, 2, -5};
int[] array2 = {-2, -8, -1, -5, -9};
int[] array3 = {};
int result1 = maxSubArray(array1);
int result2 = maxSubArray(array2);
int result3 = maxSubArray(array3);
int result11 = work(array1);
int result22 = work(array2);
int result33 = work(array3);
System.out.print("累加法:result1"+" ");
System.out.println(result1);
System.out.print("分治法:result11"+" ");
System.out.println(result11);
System.out.print("累加法:result2"+" ");
System.out.println(result2);
System.out.print("分治法:result22"+" ");
System.out.println(result22);
System.out.print("累加法:result3"+" ");
System.out.println(result3);
System.out.print("分治法:result33"+" ");
System.out.println(result33);
}
/**
* 代理,构造递归
* @param data
* @return
*/
private static int work(int[] data) {
return f(data, 0, data.length);
}
/**
* 二分法
* @param data
* @param begin
* @param end
* @return
*/
private static int f(int[] data, int begin, int end) {
// 判断数组是否为空。
if ( data.length==0 || data==null){
// System.out.println("数组为空");
return 0;
}
// 递归出口
if (end-begin==1){
// 只剩下一个元素
if (data[begin]>0){
return data[begin];
}else {
return 0;
}
}
int k = (begin+end)/2;
// 临时变量,不包含k,左闭右开
int t1 = f(data,begin,k);
int t2 = f(data,k,end);
// 中间扩展
// 中间向左边扩展
int t3a = 0;
int sum = 0;
for (int i = k-1; i>=begin; i--) {
sum += data[i];
if (sum>t3a){
t3a = sum;
}
}
// 中间向右边扩展
int t3b = 0;
sum = 0;
for (int i = k; i<end; i++) {
sum += data[i];
if (sum>t3b){
t3b = sum;
}
}
int t3 = t3a+t3b;
// 求三个数的最大值。
int max = 0;
if (t1>max){
max = t1;
}
if (t2>max){
max = t2;
}
if (t3>max){
max = t3;
}
return max;
}
public static int maxSubArray(int[] nums) {
// 判断数组是否为空。
if ( nums.length==0 || nums==null){
// System.out.println("数组为空");
return 0;
}
int sum = nums[0];
int max = sum;
for (int i = 1; i < nums.length; i++) {
if (sum<=0){
sum = nums[i];
}else {
sum += nums[i];
}
if (sum>max){
max = sum;
}
}
return max;
}
}
运行结果:
【注】
(1):leetcode 等平台只要我们完成一个函数即可,本人初出茅庐,为了巩固基本知识,故自己补充了部分代码,用于练手。本代码也许存在漏洞,望高手赐教。感谢!
(2):认真观察后,会发现对于第二个数组的结果存在差异。原因是分治法中,如果对于小于0的数,返回的是0。什么都不取。而另外一个算法,其实暗含要求最少取一个值的意思。