问题描述:输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和,求所有子数组的和的最大值。
分析:这个问题存在多种解法,各个解法的时间复杂度不一样,这里我列举三种解法。
解法一:暴力法,可以采用三重循环实现,第一重循环,记作子数组的开始数,第二种循环记作子数组的结尾数,第三重循环计算开始到结尾的和,并和目前最大和进行比较,更新最大和。
本质上就是穷举所有的子数组,然后求和。此方法的时间复杂度为O(n^3)。
对应下面代码的fun1()方法。
解法二:对解法一进行优化,当我们求出当前子数组和和时,对于下一个数,如果将其放入子数组中,总和变大了,则直接将该数加入子数组中,否则将这个数作为新的子数组的开始数,依次类推。
这种方法的时间复杂度为O(n).
对应下面代码的fun2()方法。
解法三:利用动态规划求解,关于什么是动态规划,这里不再解释,读者可以查阅其他相关资料。
我们可以设计一个函数,比如为f(i)表示以第个数字结尾的子数组的最大和,那么我们只要求出f(i)的最大值即可。可以列出以下公式:
当 i=0 或者f(i-1)<=0 f(i)=array[i]
当 i<>0并且 f(i-1)>0 f(i)=f(i-1)+array[i]
再解释下这个公式:当以第i-1个数字结尾的子数组中所有的数字的和小于等于0时,无论第i个数是什么,将他累加到子数组中,得到的结果肯定比他本身小。这时直接将这个数赋值给f(i)即可。
如果以第i-1个数字结尾的子数组中所有的数字的和大于0时,与第i个数累加就得到了以第i个数结尾的子数组中所有数字的和。
这种方法的时间复杂度为O(n).
对应下面代码的fun3()方法。
我将三种解法在一个Java类中全部实现,分别对应三个方法,都是比较通用的代码,读者可以很容易转换为其他语言实现。
1 public class Maxsum { 2 public static int fun1(int array[]){ //时间复杂度为O(n^3) 3 int n=array.length; 4 if(n<=0)return 0; //如果是空数组,直接返回0 5 int maxSum = array[0]; //最小和设为第一个数组元素的值 6 int currSum = 0; 7 for (int i = 0; i < n; i++) //子数组起点 8 { 9 for (int j = i; j < n; j++) //子数组终点 10 { 11 for (int k = i; k <= j; k++) //子数组求和 12 { 13 currSum += array[k]; 14 } 15 if (currSum > maxSum) //更新最大和 16 maxSum = currSum; 17 currSum = 0; //每次用完清空 18 } 19 } 20 return maxSum; 21 } 22 23 public static int fun2(int array[]){ //时间复杂度为O(n) 24 int n=array.length; 25 if(n<=0)return 0; //如果是空数组,直接返回0 26 int currSum = 0; 27 int maxSum = array[0]; 28 for (int j = 0; j < n; j++) 29 { 30 currSum = (array[j] > currSum + array[j]) ? array[j] : currSum + array[j]; 31 maxSum = (maxSum > currSum) ? maxSum : currSum; 32 } 33 return maxSum; 34 } 35 36 public static int fun3(int array[]){ //动态规划思想,时间复杂度为O(n) 37 int n=array.length; 38 if(n<=0)return 0; //如果是空数组,直接返回0 39 int f[]=new int[n]; 40 f[0]=array[0]; 41 int max=f[0]; 42 for(int i=1;i<f.length;i++) 43 {if(f[i-1]<=0 ||i==0)f[i]=array[i]; 44 else f[i]=f[i-1]+array[i]; 45 if(max<f[i])max=f[i]; 46 } 47 return max; 48 } 49 50 public static void main(String[] args) { 51 // TODO 自动生成的方法存根 52 int a[]={1,-2,3,10,-4,7,2,-5}; 53 System.out.print("数组为:"); 54 for(int i=0;i<a.length;i++) 55 System.out.print(a[i]+","); 56 System.out.println(); 57 System.out.println("暴力法求解答案为:"+fun1(a)); 58 System.out.println("扫描法求解答案为:"+fun2(a)); 59 System.out.println("动态规划求解答案为:"+fun3(a)); 60 } 61 62 }
输出结果为:
数组为:1,-2,3,10,-4,7,2,-5,
暴力法求解答案为:18
扫描法求解答案为:18
动态规划求解答案为:18