https://leetcode-cn.com/problems/maximum-subarray/
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
针对该问题,现给出三种解法。
解法一:暴力求解(最直观的方法)
对于长度为 n 的数组A, 任选两个位置作为区间起始和末尾,计算该区间所代表的的子数组的和,选择最大的一个。
因为需要两重循环,因此时间复杂度为O(n^2).
由于计算区间和时需要借助一个辅助数组,因此空间复杂度为O(n).
代码:
int find_max_subArray( vector<int>& A){
// 构造辅助数组,用来暂存区间和
vector<int> B = A;
for( int i = 1; i < A.size(); i++)
B[i] += B[i-1];
//暴力求解
int res = INT_MIN;
for( int i = 0; i < A.size(); i++)
for( int j = i + 1; j < A.size(); j++)
res = max( max( res, B[j] - B[i]), A[i]);
return res;
}
解法二:分治算法(不好理解且代码长)
假设将数组A[low, …, high] 以位置mid处划分为两个长度大致相等的子数组,则最大子数组A[i, …, j] 必定为以下三种情况之一:
代码:
int find_max_subArray( vector<int>& A, int left, int right){
// 处理基准情况
if( left > right)
return INT_MIN;
if(left == right) // 仅有一个元素
return A[left];
int mid = left + (right - left) / 2; // 防止溢出
int L = find_max_subArray( A, left, mid - 1); // 寻找左侧最大和
int R = find_max_subArray( A, mid + 1, right); // 寻找右侧最大和
// 跨过中间位置时
int sum = A[mid], sum_max = A[mid];
for( int i = mid - 1; i >= left; i--){
sum += A[i];
sum_max = max( sum, sum_max);
}
sum = sum_max; // 向另外一侧移动时需要初始化
for( int i = mid + 1; i <= right; i++){
sum += A[i];
sum_max = max( sum, sum_max);
}
return max( max( L, R), sum_max);
}
// 在主程序中输入的参数应该为 find_max_subArray(A, 0, A.size() - 1)
解法三:动态规划(不易理解但短小精悍)
初始化变量 sum = 0, 从头遍历数组,对于每一个元素 A[i], 变量 sum += A[i], 一旦 sum < 0, 立即让 sum = 0, 并向后移动一个元素,直到数组尾部。最终选择最大的 sum 值。
为直观起见,以数组 A = [-2, 3, -1, 2, -3] 为例:
代码:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if( nums.size() == 0)
return 0;
int res = nums[0], sum = 0;
for( auto it:nums){
// cout<<" sum = "<
if( sum < 0)
sum = 0;
sum += it;
res = max( res, sum);
}
return res;
}
};