Maximum Subarray
标签(空格分隔): algorithm
这个问题我们先看下问题的描述:
问题描述
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.
问题来自于Leetcode:Maximum Subarray
###问题分析
简单来说,就是在一个数组 A 1... n A_{1...n} A1...n中找到一个子数组 A i . . . j A_{i...j} Ai...j使得
∑ k = i j A k \sum_{k=i}^j A_k ∑k=ijAk最大,也有找最小值的(可以转化为找最大值的问题,不再详述)
- 那么最直接的想法,就是对于每一个$(i,j),i \le j $ 遍历整个数组,用一个最大值标记一下,就能都找到最大值了。对于每一个 i , j i,j i,j组合总共有 n ( n + 1 ) 2 \frac{n(n+1)}{2} 2n(n+1)个子数组,都遍历一次数组,那么可以看出来整个的复杂度为 O ( n 3 ) O(n^3) O(n3)
解决方案
1.按着上面的思路,我们可以写出如下的程序来
int maxSubArray(vector<int>& nums) {
int n = nums.size();
if(n == 0)
return 0;
if(n == 1)
return nums[0];
int max = 0x80000001;//最小的32位整数
int sum = 0;
for(int i =0;i<n;++i){
for(int j=0;j<n;++j){
sum = 0;
/*计算 A[i,j] 的和*/
for(int k=i;k<=j;++k){
sum += nums[k];
}
/**更新最大值max*/
if(sum > max)
max = sum;
}
}
return max;
}
- 但是这种方法在Leetcode上面没有通过,因为超时了。时间复杂度太高了。如果数据很大,那么会很慢。
####2.上面的解决方案1需要重复计算每个子数组的部分过程
- 上面的算法我们每次是按着 ( i , j ) (i,j) (i,j)对来计算的,如果我们当纯来想如何求所有的子数组的过程,可以发现,对于一个特定的 i i i,我们可以计算 ( i , i ) , ( i , i + 1 ) , ( i , i + 2 ) … ( i , n − 1 ) (i,i),(i,i+1),(i,i+2) \dots (i,n-1) (i,i),(i,i+1),(i,i+2)…(i,n−1)的和,
$ \sum_{k=i}^{j+1}A_k = A_{j+1} + \sum_{k=i}^{j}A_k $可以充分利用前面计算出来的 ∑ k = i j A k \sum_{k=i}^{j}A_k ∑k=ijAk,来降低时间复杂度。
- 那么就不用对于每一个 ( i , j ) (i,j) (i,j)都从 i i i到 j j j遍历数组,那么时间复杂度可以降低为 O ( n 2 ) O(n^2) O(n2)
- 所以可以有如下优化的代码
int maxSubArray(vector<int>& nums) {
int n = nums.size();
if(n == 0)
return 0;
if(n == 1)
return nums[0];
int max = 0x80000001;//最小的32位整数
int sum = 0;
for(int i=0;i<n;++i){
sum = 0;
/**对于某个特定的i 分别计算A[i,i+1],A[i,i+2],...A[i,n-1]的和*/
for(int j=i;j<n;++j){
sum += nums[j];
/**更新最大值max*/
if(sum > max )
max = sum;
}
}
return max;
}
- 噩耗再次传来,⊙﹏⊙b汗
- 没有通过leetcode测试,还是超时了
- 那么看样子时间复杂度还需要降低才可以。不然找不到工作了。。。
2.下面采用的分治的算法,从最大子数组出现的位置来考虑的。可以参考<算法导论>的第4章内容
- 分治的思想是,把数组 A i … j , i ≤ j A_{i \dots j},i \le j Ai…j,i≤j看成两个部分,可以认为是从数组中间分割成
A i … k A_{i \dots k} Ai…k 和 A k + 1 … j , k = i + j 2 A_{k+1 \dots j},k = \frac{i+j}{2} Ak+1…j,k=2i+j两个数组,那么我们的目标就是通过求这两个子数组的最大值,然后求得目前这个数组 A i … j A_{i \dots j} Ai…j的最大子数组和的值。那么问题来了,如果你知道了 A i … k A_{i \dots k} Ai…k的最大子数组的和 m a x _ l e f t max\_left max_left和 A k + 1 … j A_{k+1 \dots j} Ak+1</