1、地址
https://leetcode-cn.com/problems/maximum-subarray/
2、题面分析
题目

题目信息
- 输入是一个整数数组,返回是一个整数
- 返回的整数:一个连续子数组和
- 如果输入的数组不为空,则返回的整数不为空
3、暴力求解 - - O(N2)
求解思路
使用暴力枚举对所有可能的头部指针i和尾部指针j,求sum(nums[i,j]),记录其中最大的值放入maxSum变量中
代码实现1 - - 超出时间限制
class Solution {public int maxSubArray(int[] nums) {int len = nums.length;if(len == 1){return nums[0];}int maxSum = Integer.MIN_VALUE;int sum;for(int i = 0; i < len; i++){sum = nums[i];if(maxSum < sum){maxSum = sum;}for(int j = i + 1; j < len; j++){sum += nums[j];if(maxSum < sum){maxSum = sum;}}}return maxSum;}}
代码实现2 - - 压缩数组
虽然压缩了数组但是还是慢
class Solution {public int maxSubArray(int[] nums) {int len = nums.length;if(len == 1){return nums[0];}int startIndex = 0;int tempSum = nums[0];// 应对全负数int max = tempSum;for(int i = 1; i < len; i++){if(nums[i] > max){max = nums[i];}if((tempSum <= 0 && nums[i] <= 0) || (tempSum >= 0 && nums[i] >= 0 ) ){tempSum += nums[i];}else{nums[startIndex++] = tempSum;tempSum = nums[i];}}nums[startIndex++] = tempSum;len = startIndex;int maxSum = Integer.MIN_VALUE;int sum;for(int i = 0; i < len; i++){sum = nums[i];if(maxSum < sum){maxSum = sum;}for(int j = i + 1; j < len; j++){sum += nums[j];if(maxSum < sum){maxSum = sum;}}}return maxSum > max ? maxSum : max;}}
4、贪心算法
求解思路
从下标0开始累加并更新maxSum,当加到某个数使得当前和<0,从下一个数开始重新计算加和.
代码实现
class Solution {public int maxSubArray(int[] nums) {int len = nums.length;if(len == 1){return nums[0];}int maxSum = Integer.MIN_VALUE;int sum = 0;for(int i = 0; i < len; i++){sum += nums[i];if(sum > maxSum){maxSum = sum;}if(sum < 0){sum = 0;}}return maxSum;}}
5、动态规划算法
求解思路
共同点
基于子问题求解(无后效性)
- 策略驱动
不同点:
- 贪心:
- 局部最优可以得到全局最优
- 解具备一定的单调性
- 动态规划:
- 每个子问题可能会记录多种状态,不一定只存储最优
- 对解的单调性要求不高
动态规划方程:
DP(k) = f(DP(k-1), element(k))
DP(k):表示nums[0,k]
代码实现
class Solution {public int maxSubArray(int[] nums) {int len = nums.length;if(len == 1){return nums[0];}int[] dp = new int[len];dp[0] = nums[0];int maxSum = dp[0];for(int i = 1; i < len; i++){dp[i] = Math.max(dp[i-1] + nums[i], nums[i]);if(dp[i] > maxSum){maxSum = dp[i];}}return maxSum;}}
6、分治法
- 问题递归分割:分治法会将原问题分成N个子问题分别进行求解。如果子问题的规模依旧非常大(不能直观的得到答案的规模),那么需要对子问题递归的进行分割
- 临界问题求解:可以“简单”求解的问题。一般对临界问题的求解消耗的时间都是O(1)
- 子问题解的合并:当已知子问题的解时,通过这些解得到父问题的答案
算法核心:将大化小,对小问题进行求解,从而求解出原问题
算法难点:如何分割,以及如何将解合并
分治法的重要思想:假设当前已经拥有子问题的解
求解思路
- 子问题的解与父问题的解
- 父问题S的解在左实例S1中:递归求解S1
- 父问题S的解在右实例S2中:递归求解S2
- 父问题S的解被分在左右两个实例中:
- 在S1中,求解是以i作为解的右下标解区间
- 在S2中,求解时以i+1作为解的左下标的解区间
代码实现
class Solution {public int maxSubArray(int[] nums) {int len = nums.length;if(len == 1){return nums[0];}return maxSumSubArray(nums, 0, len - 1);}public int maxSumSubArray(int[] nums, int leftIndex, int rightIndex){int arrayLen = rightIndex - leftIndex + 1;if(arrayLen == 1){return nums[leftIndex];}int midIndex = (rightIndex - leftIndex) / 2 + leftIndex;int rightMaxSub = maxSumSubArray(nums, midIndex + 1, rightIndex);int leftMaxSub = maxSumSubArray(nums, leftIndex, midIndex);int midMaxSub = maxSumContainsMidIndex(nums, midIndex,leftIndex, rightIndex);return Math.max(midMaxSub, Math.max(leftMaxSub, rightMaxSub));}public int maxSumContainsMidIndex(int[] nums, int midIndex, int leftIndex, int rightIndex){int leftMax = Integer.MIN_VALUE;int rightMax = leftMax;int sum = 0;if(leftIndex < midIndex){for(int i = midIndex; i >= leftIndex; i-- ){sum += nums[i];if(sum > leftMax){leftMax = sum;}}}else{leftMax = nums[midIndex];}if(rightIndex > midIndex){sum = 0;for(int i = midIndex + 1; i <= rightIndex; i++ ){sum += nums[i];if(sum > rightMax){rightMax = sum;}}}else{rightMax = 0;}return leftMax + rightMax;}}
