LeetCode题解 #53 Maximum Subarray
源暗 发布于 9月19日 0评论 880浏览leetcode 0 1
- 关键字:分治
- 难度:中
- 题目大意:对于给定的一段序列,找出其中和最大的一段连续子序列。
题目描述
Find the contiguous subarray within an array (containing at least one number) which has the largest sum.
For example, given the array [−2,1,−3,4,−1,2,1,−5,4],
the contiguous subarray [4,−1,2,1] has the largest sum = 6.
解法
这道题目其实存在一个非常简单的O(n)复杂度的算法,但是无论是标签还是题后的说明都在试图让我们使用分治来做这道题,那么好吧,笔者就来讲述一下如何用分治解决这道题目。
首先,我们需要将问题抽象化一下,即如果我们用n表示序列的长度,用f(l, r)表示区间左右端点均在[l, r]范围内的所有连续子序列中序列和的最大值,那么我们要求的答案其实就是f(0, n - 1)。
那么如何求解f(l, r)呢?像之前所说,我们使用分治,即将一个大问题分解成若干小问题的方法来解决!
不妨设 m = (l + r) / 2,即区间的中心点,那么对于这个区间中和最大的子区间来说,只存在两种可能:要么这个子区间穿过了m,要么没有穿过。
- 如果穿过了m的话,我们就只需要找到左半部分区间中从m开始向左“区间和”最大的区间,和右半部分区间中从m开始向右“区间和”最大的区间,然后将这两个区间拼起来,就可以得到这个子区间(可以这样寻找这两个区间,即令sum[i] = nums[0] + nums[1] + ... + nums[i],即nums的前缀和,这样就只需要找sum[l .. m]中的最小值,和sum[m + 1 .. r]中的最大值即可)。
- 如果没有穿过m的话,那么一定完全处于左半部分区间中,或者完全处于右半部分区间中,我们只需要计算f(l, m)和f(m + 1, r)的较大值即可。
对于这样的两种方案,我们依次求出满足其设定的“最大”区间,然后进行比较,选出其中“最大”的区间即可。
由于问题的规模每次缩小一半,不难发现规模为n的问题有1个,规模为n/2的问题有2个,规模为n/4的问题有4个……,依次类推,我们可以知道最后问题的复杂度为O(nlogn),这样这道问题就可以得到完美的解决!
参考代码
class Solution {
public:
int maxSubArray(vector<int>& nums) {
// 计算nums的前缀和
for (int i = 1; i < nums.size(); i++) {
nums[i] = nums[i - 1] + nums[i];
}
// 分治的去计算答案
int smin, smax;
return calc_max(nums, 0, nums.size() - 1, smin, smax);
}
// 抽象问题:区间范围在[l, r]内的最大区间是多少
int calc_max(vector<int> &nums, int l, int r, int &smin, int &smax) {
if (l == r) {
// 边界情况
smin = smax = nums[l];
return nums[l];
}
else {
int lmin, lmax, rmin, rmax, tmp, m = (l + r) / 2;
// 第一种可能,答案完全处于左半部分
int ans = calc_max(nums, l, m, lmin, lmax);
// 第二种可能,答案完全处于右半部分
ans = max(ans, calc_max(nums, m + 1, r, rmin, rmax));
// 第三种可能,答案横跨了两个部分
ans = max(ans, rmax - lmin);
// 计算当前区间中的最大值和最小值
smin = min(lmin, rmin);
smax = max(lmax, rmax);
// 返回最大值
return ans;
}
}
};
转载自源暗,天码营