携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天。
题目链接: leetcode.com/problems/ma…
1. 题目介绍(Maximum Subarray)
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
【Translate】: 给定一个整数数组nums,找到具有最大和的连续子数组(至少包含一个数字)并返回其和。
A subarray is a contiguous part of an array.
【Translate】: 子数组是数组的连续部分。
【测试用例】:
【约束】:
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.
【Translate】: 如果你已经想出了O(n)的解决方案,尝试使用分治方法编写另一个解决方案,这是更微妙的。
2. 题解
2.1 穷举 -- O(n^2^)
该题很容易就能想到穷举,简单易行,依次遍历所有可能,找出连续相加和中最大的数,但就是时间复杂度太高了。
class Solution {
public int maxSubArray(int[] nums) {
int n = nums.length;
int maxSum = Integer.MIN_VALUE;
for (int i = 0; i < n; i++)
{
int sum = 0;
for (int j = i; j < n; j++)
{
sum = sum + nums[j];
if (sum > maxSum)
{
maxSum = sum;
}
}
}
return maxSum;
}
}
2.2 一次遍历 -- O(n)
原题解来自于Chaitanya1706的JAVA | Kadane's Algorithm | Explanation Using Image。该题解的思想如下图所示,顺序遍历所有元素,通过sum记录逐渐相加的和,通过max记录当前最大值。这里值得注意的一点是,如果在任意一点sum变为负数,那么就没有必要再保留它了,因为0显然大于负,所以求和为0(不要怕会改变最后结果,因为下一次循环,sum又被赋予了新值)。
class Solution {
public int maxSubArray(int[] nums) {
int n = nums.length;
int max = Integer.MIN_VALUE, sum = 0;
for(int i=0;i<n;i++){
sum += nums[i];
max = Math.max(sum,max);
if(sum<0) sum = 0;
}
return max;
}
}
2.3 分治 -- O(N\log{N})
Divide-and-conquer consider 3 cases:
- Case 1: Subarray in the left half -> leftSum
- Case 2: Subarray in the right half -> rightSum
- Case 3: Subarray crosses the middle -> crossSum
原题解来自于junhaowanggg的Easy-Understand Java Solutions with Explanations (B-F, Divide-And-Conquer, DP)。
class Solution {
public int maxSubArray(int[] nums) {
int n = nums.length;
return maxSubArray(nums, 0, n - 1);
}
private int maxSubArray(int[] nums, int lo, int hi) {
if (lo == hi) { // base case: one number
return nums[lo];
}
// divide
int mid = lo + (hi - lo) / 2;
// conquer
int leftSum = maxSubArray(nums, lo, mid);
int rightSum = maxSubArray(nums, mid + 1, hi);
// combine
int crossSum = crossSum(nums, lo, hi);
return Math.max(crossSum, Math.max(leftSum, rightSum));
}
// invariant: lo < hi (left part and right part both have at least 1 element
private int crossSum(int[] nums, int lo, int hi) {
int mid = lo + (hi - lo) / 2;
// left
int leftSum = 0, leftMax = Integer.MIN_VALUE; // the invariant means that leftMax and rightMax will be updated
for (int i = mid; i >= lo; --i) {
leftSum += nums[i];
leftMax = Math.max(leftMax, leftSum);
}
// right
int rightSum = 0, rightMax = Integer.MIN_VALUE;
for (int i = mid + 1; i <= hi; ++i) {
rightSum += nums[i];
rightMax = Math.max(rightMax, rightSum);
}
return leftMax + rightMax;
}
}