来源:力扣(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;
}
}
思路四:
前缀和解法,,,未完待续...