连续子数组的最大和
题目
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
思路
思路1
首先,先用动态规划的思想来考虑。
假设f(k) 给出以数组的第 k个元素为结尾的 最大连续子序列和,则f(k)可定义如下,f(0)=arr[0]:
if (f(k-1)>0)
f(k) = arr[k] + f(k-1)
else
f(k) = arr[k]
基于f(k),循环arr数组的每一个元素,比较以哪一个元素结尾的 f(k)最大,就好了。
但是这样的方法 消耗巨大。并且有重复的计算。
思路2
所以,从低往上,不采用递归,而是 依次计算 f(0), f(1), 并且记录 这过程中的最大值。
-
维护一个currentSum 和 maxSum
-
对于一个元素,如果前面的sum加上当前val 还要小于 val,那么不如从当前开始。不然就,加上该元素。
-
每加一次,判断maxsum是否需要更新。
实现
//思路1的实现
class Solution {
public:
int fSequenceSum(vector<int>& array, int index)
{
if(index==0)
return array[index];
int k = fSequenceSum(array, index-1);
if(k <= 0)
return array[index];
else
return k+array[index];
}
int FindGreatestSumOfSubArray(vector<int> array) {
if(array.empty()) return 0;
//size 1 时
int maxsum = array[0];
for(int i=1; i<array.size(); ++i)
{
int tmp = fSequenceSum(array, i);
maxsum = tmp>maxsum ? tmp : maxsum;
}
return maxsum;
}
};
//思路2 的实现
class Solution {
public:
int FindGreatestSumOfSubArray(vector<int> array) {
if(array.empty()) return 0;
int currentSum = array[0];
int maxSum = currentSum;
for(int i=1; i<array.size(); ++i)
{
int val = array[i];
if((val + currentSum) < val)
currentSum = val;
else
currentSum = val + currentSum;
if(currentSum>maxSum)
maxSum = currentSum;
}
return maxSum;
}
};
思路3,分而治之
https://www.bilibili.com/video/av18586085?p=9
#include <limits.h>
class Solution {
public:
//分治法
/* 1.不断拆分,当前数组的最大子串和,等于 左边子串的最大子串和,右边子串的最大子串和 和 cross左右子串的最大值 中的 最大值
https://www.bilibili.com/video/av18586085?p=9 */
int maxSubArray(vector<int>& nums) {
int result = INT_MIN;
if(nums.empty()) return result;
result = maxSubArrayCore(nums, 0, nums.size()-1);
return result;
}
int maxSubArrayCore(vector<int>& nums, int left, int right)
{
if(left==right) return nums[left];
int mid = (left+right) / 2;
int leftMax = maxSubArrayCore(nums, left, mid);
int rightMax = maxSubArrayCore(nums, mid+1, right);
int crossMax = crossSubArrayMax(nums, left, right, mid);
//返回 左边子串的最大子串和,右边子串的最大子串和 和 cross左右子串的最大值 中的 最大值
return max(leftMax, max(rightMax, crossMax));
}
int crossSubArrayMax(vector<int>& nums, int left, int right, int mid)
{
//start from mid, 分别向两边扩展 <--mid-->
int leftSum = nums[mid];
int leftSumMax = leftSum; //[left, mid]
for(int i=mid-1; i>=left; --i)
{
leftSum += nums[i];
leftSumMax = max(leftSumMax, leftSum);
}
int rightSum = nums[mid+1];
int rightSumMax = rightSum;//[mid+1, right]
for(int i=mid+2; i<=right; ++i)
{
rightSum += nums[i];
rightSumMax = max(rightSumMax, rightSum);
}
return leftSumMax + rightSumMax;
}
};