连续子数组的最大和

小灰灰 2022-05-14 09:14 294阅读 0赞

题目描述

HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)

剑指offer上的题目描述:

输入一个整形数组,数组里有整数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。

例如,输入的数组为{1,-2,3,10,-4,7,2,-5},和最大的子数组为{3,10,-4,7,2},因此输出为该数组的和18。

一般看到这道题,能想到的最直观的方法枚举数组的所有子数组并求出它们的和。一个长度为n的数组,总共有n(n+1)/2个子数组。计算出所有子数组的和,最快也需要O(n^2)的时间。通常最直观的方法不会是最优的解法,应该还有更快的算法。

解法一:举例分析数组的规律

从头到尾逐个累加数组中的每个数字,初始化和为0;(nCurrSum=0,nGreatestNum=int.MinValue)

定义两个变量:“累加子数组和”和“最大子数组和”,第一步把数组中的第一个数字赋值给他们,然后从第二个数字开始累加,累加值放入“累加子数组和”。

1.如果当前“累加子数组和”小于0,那抛弃前面的“累加子数组和”,从下一个数字开始重新累加,“最大子数组和”的值不更新。

2.如果当前“累加子数组和”大于0,再让当前“累加子数组和”和当前的“最大子数组和”进行比较。

  1. 2.1如果当前“累加子数组和”**大于**当前“最大子数组和”,则更新“最大子数组和”的值为“累加子数组和”的值。
  2. 2.2如果当前“累加子数组和”**小于**当前“最大子数组和”,“最大子数组和”的值不更新。

3.再加入数组中的下一个值,“累加子数组和”进入下一轮的累加,“最大子数组和”也进入下一轮的更新。知道数组中所有值都累加完毕。

这样进行一次累加的遍历之后,就可以得到最终的最大累加和,时间复杂度是O(n)。下图展示了数组{1,-2,3,10,-4,7,2,-5}求连续子数组的最大和的过程:

70

把过程分析清楚之后,我们就可以动手写代码了。

方法一利用循环求连续子数组的最大和的Java代码如下:

  1. public class Solution {
  2. public static void main(String[] args) {
  3. int[] array = {1,-2,3,10,-4,7,2,-5};
  4. Long begintime = System.nanoTime();
  5. int result = FindGreatestSumOfSubArray(array);
  6. Long endtime = System.nanoTime();
  7. System.out.println("连续子数组的最大和为:"+result+",运行时间:"+(endtime - begintime) + "ns");
  8. }
  9. public static int FindGreatestSumOfSubArray(int[] array) {
  10. int len = array.length;
  11. if (len == 0){
  12. return 0;
  13. }
  14. int currentsum = array[0];
  15. int greatsetsum = array[0];
  16. System.out.println("第1步:累加子数组和:"+currentsum+",最大子数组和:"+greatsetsum);
  17. for(int i=1;i<array.length;i++){
  18. if(currentsum > 0){
  19. currentsum += array[i];
  20. }else{
  21. currentsum = array[i];
  22. }
  23. if(currentsum > greatsetsum){
  24. greatsetsum = currentsum;
  25. }
  26. System.out.println("第"+(i+1)+"步:累加子数组和:"+currentsum+",最大子数组和:"+greatsetsum);
  27. }
  28. return greatsetsum;
  29. }
  30. }

程序运行结果:

70 1

从运行结果可以看到,和上面分析的结果是一样的。

解法二:应用动态规划法

除了上面的解法,我们还可以用动态规划的思想来分析这个问题。如果用函数f(i)表示以第i个数字结尾的子数组的最大和,那么我们需要求出max[f(i)],其中0 <= i <= n。我们可以用如下递归公式求f(i):

f(i) = pData[i] i = 0或者f(i-1) <= 0

f(i) = f(i-1) + pData[i] i 不等于 0并且f(i-1) > 0

这个公式的意义:当以第I-1个数字结尾的子数组中所有数字的和小于0时,如果把这个负数与第i个数累加,则得到的结果比第i个数字本身还要小,所以这种情况下以第I个数字结尾的子数组就是第I个数字本身。如果以第I-1个数字结尾的子数组中所有数字的和大于0,则与第i个数字累加就得到以第i个数字结尾的子数组中所有数字的和。

虽然通常我们用递归的方式分析动态规划的问题,但最终都会基于循环去编码。上述公式对应的代码与前面给出的代码类似。递归公式中的f(i)对应的变量是“累加子数组和”currentsum,而max[f(i)]就是“最大子数组和”greatsetsum。因此,可以说这两种思路是异曲同工的。

方法二利用动态规划求连续子数组的最大和的Java代码如下:

  1. public class Solution {
  2. public static void main(String[] args) {
  3. int[] array = {1,-2,3,10,-4,7,2,-5};
  4. Long begintime = System.nanoTime();
  5. int result = FindGreatestSumOfSubArray(array);
  6. Long endtime = System.nanoTime();
  7. System.out.println("连续子数组的最大和为:"+result+",运行时间:"+(endtime - begintime) + "ns");
  8. }
  9. public static int FindGreatestSumOfSubArray(int[] array) {
  10. int len = array.length;
  11. if (len == 0){
  12. return 0;
  13. }
  14. int[] currentsum = new int[len];
  15. currentsum[0] = array[0];
  16. int greatsetsum = array[0];
  17. System.out.println("第1步:累加子数组和:"+currentsum[0]+",最大子数组和:"+greatsetsum);
  18. for(int i=1;i<array.length;i++){
  19. //下面是动态规划的状态转移方程
  20. if(currentsum[i-1]>0){
  21. currentsum[i] = currentsum[i-1] + array[i];
  22. }else{
  23. currentsum[i] = array[i];
  24. }
  25. //根据currentsum的值更新greatsetsum的值
  26. if(currentsum[i] > greatsetsum){
  27. greatsetsum = currentsum[i];
  28. }
  29. System.out.println("第"+(i+1)+"步:累加子数组和:"+currentsum[i]+",最大子数组和:"+greatsetsum);
  30. }
  31. return greatsetsum;
  32. }
  33. }

程序运行结果:

70 2

可以看到该程序的运行结果和上面的是一样的。

使用动态规划方法求解连续子数组的最大和的时间复杂度也是O(n)

发表评论

表情:
评论列表 (有 0 条评论,294人围观)

还没有评论,来说两句吧...

相关阅读

    相关 连续

    HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。

    相关 连续

    最大连续子数组和 1. 题目描述 输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。 求所有

    相关 连续

    题目描述 HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候

    相关 连续

    题目描述 HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候