Leetcode Algorithm #53 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.
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.
解题
很惭愧这样的问题都只想出了一个 O(nlogn) O ( n l o g n ) 的解决方法,感觉脑子好久没有使用了。一开始的确也是首先考虑 O(n) O ( n ) 的方法,利用标志位啥的,但想来想去都发现是遗漏了情况。于是使用了分治递归的方法,可以说是比较偷懒,因为分治的方法比较容易想清楚,虽然在有些情况下分治的是比较快的,但是在这题并不是。
分治方法可以这样考虑,将数组分为两边,最大的区间只会出现在三种情况:
- 完全在左边
- 完全在右边
- 横跨两个区域
对于第三种情况,需要 O(n) O ( n ) 的计算时间,因为要横跨的话这个区域必定是紧挨着边界,所以只要从边界出发,分别求两侧的最大子串再进行加和即可。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
return divide(0, nums.size() - 1, nums);
}
private:
int divide(int head, int rear, vector<int>& nums) {
if (head == rear)
return nums[head];
int mid = (head + rear) / 2;
int r1 = divide(head, mid, nums);
int r2 = divide(mid + 1, rear, nums);
int temp_sum = nums[mid], max1 = nums[mid], max2 = nums[mid+1];
for (int i = mid - 1; i >= 0; --i)
{
temp_sum += nums[i];
if (temp_sum > max1)
max1 = temp_sum;
}
temp_sum = max2;
for (int i = mid + 2; i <= rear; ++i)
{
temp_sum += nums[i];
if (temp_sum > max2)
max2 = temp_sum;
}
return max(r1, r2, max1 + max2);
}
int max(int a, int b, int c) {
if (a > b)
{
if (a > c)
return a;
else
return c;
}
else
{
if (b > c)
return b;
else
return c;
}
}
};
当然结果就是超级慢,都超过图表中最慢的边界了。
改进
之前考虑不清楚,主要就是总是把当前最大和结尾看作两个部分,考虑如何选择这两个部分,但有可能这个区间出现在两者的中央。实际并不需要这么做。所要考虑的仅仅是一个连续和为正数区间即可,参考GeeksforGeeks。首先有一个ending
标记和max_found
标记,分别代表在当前元素之前的最长连续元素使得和为正数的值,以及在当前元素之前最大的子串和。若当前位置为i
,将ending
位置元素加上nums[i]
成为新ending
,可以考虑如果ending
为负,则说明前方元素加该元素没必要和后面的元素进行组合了,而前方最大的子串已经被记录在max_found
中,所以舍弃这个值并没有问题,将ending
值重新置为0
。显而易见,max_found
只要每次都和ending
做比较,记录其最大值即可。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int size = nums.size(), found = std::numeric_limits<int>::min(), ending = 0;
for (int i = 0; i < size; ++i)
{
ending += nums[i];
if (ending > found)
found = ending;
if (ending < 0)
ending = 0;
}
return found;
}
};