剑指 Offer 42. 连续子数组的最大和

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof

题目描述:

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

示例1:

输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

思路一:

暴力扫描,遍历找出所有的子数组和,判断找出一个最大。

代码实现:
class Solution {
    public int maxNum = Integer.MIN_VALUE;
    public int maxSubArray(int[] nums) {
        int len = nums.length;
       for (int i  = 0; i < len; i++) {
           int sum = 0;
           for (int j = i; j < len; j++) {
               sum += nums[j];
               if (sum > maxNum) {
                   maxNum = sum;
               }
           }
       }
       return maxNum;
    }
}
思路二:

状态置换方程:dp[i] = Math.max((dp[i – 1] + arr[i]), arr[i]);
动态规划:计算子数组之和的时候必须保证每次求和都是正向的.例如数组arr{1, -3, -2, 5, 7}

初始化sum = 0
遍历arr[i]
sum + arr[0] = 0 + 1, sum = 1, sum >= arr[0];
sum + arr[1] = 1 + (-3), sum = -2, sum >= arr[1];
sum + arr [2] = -2 + (-2), sum = -4, -4 < -2, 此时sum小于a[2] , 则 sum + arr[3]一定小于arr[2] + arr[3]; 
所以另sum = arr[2],也即是sum = Math.max((sum + arr[i]), arr[i])
class Solution {
    
    public int maxSubArray(int[] nums) {
      int maxNum = Integer.MIN_VALUE;
        int sum = 0;
        for (int num : nums) {
            sum = Math.max((sum + num), num);
            if (sum > maxNum) {
                maxNum = sum;
            }
        }
        return maxNum;
    }
}
思路三:

分治法:选择一个基准点将数组分为3部分(一般选择中点),那么最大子数组之和可能存在的位置就只有3种.
左边的子数组:leftArr
右边的子数组:rightArr
左边子数组和右边子数组组成的新数组中(一部分在左边子数组,一部分在右边子数组):middleArr

重复上面的操作,直到子数组的长度为1,再比较leftArr,rightArr,middleArr的大小,返回最大值.

class Solution {
    public int maxNum = Integer.MIN_VALUE;
    public int maxSubArray(int[] nums) {
        int len = nums.length - 1;
        return sumChildArr(nums, 0, len);
    }
  
    public  int sumChildArr(int[] arr, int start, int end) {
        if (start == end) {
            return arr[start];
        }

        int middle = (start + end) / 2;
        int maxLeft = sumChildArr(arr, start, middle);
        int maxRight = sumChildArr(arr, middle + 1, end);
        int maxMiddle = sumChildArrMiddle(arr, start, middle, end);
        if (maxLeft >= maxRight && maxLeft >= maxMiddle) {
            return maxLeft;
        } else if (maxRight > maxLeft && maxRight > maxMiddle) {
            return maxRight;
        } else {
            return maxMiddle;
        }
    }

   public static int sumChildArrMiddle(int[] arr, int start, int middle, int end) {
        int maxLeftNum = Integer.MIN_VALUE;
        int maxRightNum = Integer.MIN_VALUE;
        // 求左边子数组右边界最大之和.
        int sum = 0;
        for (int i = middle; i >= start; i--) {
            sum += arr[i];
            if (maxLeftNum < sum) {
                maxLeftNum = sum;
            }
        }
        // 求右子数组左边界最大值和.
        sum = 0;
        for (int i = (middle + 1); i <= end; i++) {
            sum += arr[i];
            if (maxRightNum < sum) {
                maxRightNum = sum;
            }
        }
        // 中间子数组最大之和.
        return maxLeftNum + maxRightNum;
    }
}
思路四:

前缀和解法,,,未完待续...

你可能感兴趣的:(剑指 Offer 42. 连续子数组的最大和)