数组最大的连续子序列和的问题
首先设数组为vector nums(n);即n长度的整型数组。
对于最大连续子序列的和的问题,有暴力法和二分法两种很普遍的方法,但是实际上还有另一种方法,可以在线性时间内得到结果。
在这个问题中,我们实际上需要确定的是连续序列的起始点和终止点。
我们不知道终止点,但我们可以考虑分别将以0,1,…,n-1为终止点的最大连续序列和算出来,那么最大的那个即为结果,如果我们定义f(i)为[0,i]区间上以nums[i]为终止点的最大连续和,f(i)可以表示为max(sum(nums[x:i]))(for x=0,1,…,i),即存在某个x,使[x,i]上和最大
按照MATLAB向量表示法,nums[x:y]表示索引对应的元素值组成的新数组,sum表示求和
按照f(i)的定义可知整个数组最大的连续子序列和是max{f(i)}(for i=0,1,…,n-1).为了获得所有f(i),势必得到f(i+1)与f(i)的关系
问题:f(i+1)跟f(i)的关系是怎样的呢?
f(i+1)是以nums[i+1]为终止点的,那么要想f(i+1)取得最大,起始点的选取x’只有两种假设:
1.假设x’<=i,f(i+1)=max(sum(nums[x’:i+1]))=nums[i+1]+max(sum(nums[x’:i]))=nums[i+1]+f(i).此时x’可知应该等于x
2.x’>i,那么x’只能等于i+1,即f(i+1)=nums[i+1],此时x’可知应该等于i+1
所以,x’到底怎么选使f(i+1)满足定义需要判断nums[i+1]与nums[i+1]+f(i)谁大谁小,即f(i)是否大于0
由此,可得递归关系
f(i+1)=f(i)>0?f(i)+nums[i+1]:nums[i+1];
x’=f(i)>0?x:i+1;
为了得到最后结果,可设一个记录max=f(0)=nums[0];每次迭代时,通过判断f(i)与max的大小关系而使max始终保持最大。另外,如果想要获取具体的区间,可以设置两个索引 left_stack=0,left=0和right=0,每次如果f(i)<0,更新left_stack=i+1,而每次需要更新max时,left=left_stack,right=i+1
C++代码如下:需要的头文件是vector、assert.h
struct RESULT {
int left, right;
int max;
};
RESULT getMaxSubVectorSum(vector<int>const&vi)
{
int const Len = vi.size();
assert(Len > 0);
int dp = vi[0];
int left_stack = 0;
RESULT res{ 0, 0, vi[0] };
for (int i = 1; i < Len; ++i) {
//for nums[i]:
if (dp > 0) {
dp += vi[i];
} else {
dp = vi[i];
left_stack = i;
}
if (dp > res.max) {
res.max = dp;
res.left = left_stack;
res.right = i;
}
}
return res;
}
Thinking:通常提出f(i)的表示式的时候,我们很容易会被x如何选取带进坑中,但事实上,我们当务之急只需要直接考虑f(i)的递归表达式,因为解决了所有i上的f(i),整个问题就解决了,在这个过程中,x的选取的方法也会明晰出来