LeetCode 53. 最大子数组和(四种解题思路)包含扩展返回最大和的数组
子数组是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
思路
暴力解法:
两层for循环,第一层for循环设置起始位置,第二层for循环遍历数组,时间复杂度On方
class Solution {public int maxSubArray(int[] nums) {int res = Integer.MIN_VALUE;for (int i = 0; i < nums.length; i++) {int count = 0;for (int j = i; j < nums.length; j++) {count += nums[j];res = Math.max(res, count);}}return res;}
}
贪心解法:
如果 -2 1 在一起,计算起点的时候,一定是从 1 开始计算,因为负数只会拉低总和,这就是贪心贪的地方!
局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素的话“连续和”只会越来越小。
全局最优:选取最大“连续和”
从代码角度上来讲:遍历 nums,从头开始用 count 累积,用result记录最大和的终止位置,如果 count 一旦加上 nums[i]变为负数,那么就应该从 nums[i+1]开始从 0 累积 count 了,因为已经变为负数的 count,只会拖累总和。
class Solution {public int maxSubArray(int[] nums) {int res = Integer.MIN_VALUE;int count = 0;for (int i = 0; i < nums.length; i++) {count += nums[i];res = Math.max(res, count);if (count < 0) count = 0;}return res;}
}
前缀和
因为子数组的和就是前缀和-最小前缀和的差,所以我们可以在一层for循环中,先计算前缀和-最小前缀和,再更新最小前缀和
class Solution {public int maxSubArray(int[] nums) {int res = Integer.MIN_VALUE;int sum = 0;int preSum = 0;for (int i = 0; i < nums.length; i++) {sum += nums[i]; //记录前缀和res = Math.max(res, sum - preSum); //记录结果preSum = Math.min(preSum, sum); // 更新最小前缀和}return res;}
}
动态规划
- 确定dp数组以及下标含义:
dp[i]
:表示以nums[i]
结尾 的 连续 子数组的最大和。 - 递推公式:dp[i]只有两个方向可以推出来:
- dp[i - 1] + nums[i],即:nums[i]加入子数组和
- nums[i],即:从头开始计算子数组和
- 初始化:dp[0] = nums[0]
- 遍历顺序:从左往右for循环遍历
class Solution {public int maxSubArray(int[] nums) {// dp[i]:以下标i结尾的最大子数组和int[] dp = new int[nums.length];dp[0] = nums[0];int res = dp[0];for (int i = 1; i < nums.length; i++) {if (dp[i - 1] > 0) {dp[i] = dp[i - 1] + nums[i];} else {dp[i] = nums[i];}res = Math.max(res, dp[i]);}return res;}
}
如果是返回最大和的最数组呢?
应用动态规划的解题思路
class Solution {public int maxSubArray(int[] nums) {// dp[i]:以下标i结尾的最大子数组和int[] dp = new int[nums.length];dp[0] = nums[0];int res = dp[0];int maxStart = 0, maxLen = 1; //记录最大子数组起点和长度int start = 0, len = 1; //记录子数组的起点和长度for (int i = 1; i < nums.length; i++) {if (dp[i - 1] > 0) {dp[i] = dp[i - 1] + nums[i];len++;} else {dp[i] = nums[i];start = i;len = 1;}if (dp[i] > res) {res = dp[i];maxStart = start;maxLen = len;}}System.out.println(maxLen);System.out.println(Arrays.toString(Arrays.copyOfRange(nums, maxStart, maxStart + maxLen)));return res;}
}