53. Maximum Subarray & 918. Maximum Sum Circular Subarray
Given an integer array nums
, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Example:
Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
Follow up:
If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
My Solutions:
在每一步维护两个变量,一个是全局最优,就是到当前元素为止最优的解。一个是局部最优,就是必须包含当前元素的最优解
方法1:dp,留住当前数字最大的可能性,可以用dp【】储存(见comment out部分)
Time: O(n); Space: O(n)
优化1: 优化一下用int temp储存局部最大值,用int max储存全局最大值。
Time: 不变;Space: O(1)
class Solution {
public int maxSubArray(int[] nums) {
if (nums == null || nums.length <= 0) {
return 0;
}
// 方法1
// int n = nums.length;
// int[] dp = new int[n]; // dp[i] means the maximum subarray ending with nums[i]
// dp[0] = nums[0];
// int max = dp[0];
// for(int i = 1; i < n; i++){
// // dp[i]是包含当前位置的最优解
// dp[i] = Math.max(nums[i] + dp[i - 1] , nums[i]);
// max = Math.max(max, dp[i]);
// }
// return max;
// 优化1.1
int max = nums[0];
int temp = nums[0];
for (int i = 1; i < nums.length; i++) {
// temp records the max of (curr element + subarray before curr element) OR current element so far
temp = Math.max(temp + nums[i], nums[i]); //局部最佳
max = Math.max(temp, max); //全局最佳
}
return max;
// 优化1.2
int max = Integer.MIN_VALUE, temp = 0;
for (int n : nums) {
if (n + temp > n) temp += n; // temp + n 是最优解
else temp = n; // 只有n是最优解
max = Math.max(max, temp); // max records the largest subarray
}
return max;
// e.g. [5,4,-1,7]
// at i = 0, temp = 5, max = 5
// at i = 1, temp = 9, max = 9
// at i = 2, temp = 8, max = 9
// at i = 3, temp = 15, max = 15
}
}
918.Maxium Sum Circular Subarray
和#53类似,但是可以首尾相连。
Example 2:
Input: nums = [5,-3,5]
Output: 10
Explanation: Subarray [5,5] has maximum sum 5 + 5 = 10.
Example 3:
Input: nums = [-3,-2,-3]
Output: -2
Explanation: Subarray [-2] has maximum sum -2.
My Solutions:
因为首尾相连,结果可能有两种情况:
最大子数组是原数组的子数组或本身 --> 和#53题一样
最大子数组是由原数组的前缀子数组以及后缀子数组组合而成 --> 换一个思路,可以在原数组中找到一个最小子串和,然后再使用原数组的总和减去这个最小子串和,即可得到一个最大的环形子串。找到最小子串和的逻辑和最大子串和的方法相反
方法1:
public int maxSubarraySumCircular(int[] nums) {
// case 1
int max = Integer.MIN_VALUE, temp = 0;
for (int n : nums) {
// sum records the current element OR (curr element + subarray) so far
if (temp + n > n) temp += n;
else temp = n;
max = Math.max(max, temp);
}
// case 2
temp = 0;
int sum = 0, min = Integer.MAX_VALUE;
for (int n : nums) {
sum += n;
if (temp + n > n) temp = n;
else temp += n;
min = Math.min(min, temp);
}
if (min == allSum) return max; // if all numbers are negative
return Math.max(max, sum - min);
}
优化1: 观察一下,其实两次遍历可以减少至一次,因为每次都在重新计算temp。注意这里的max和min是第一个数字。
int sum = 0, max = nums[0], currMax = 0, min = nums[0], currMin = 0;
for (int n : nums) {
sum += n;
currMax = Math.max(currMax, 0) + n;
max = Math.max(max, currMax);
currMin = Math.min(currMin, 0) + n;
min = Math.min(min, currMin);
}
return sum == min ? max : Math.max(max, sum - min);
Last updated