最大子数组问题 Maximum Subarray

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,n1)的和,
    $ \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 Aij,ij看成两个部分,可以认为是从数组中间分割成
    A i … k A_{i \dots k} Aik A k + 1 … j , k = i + j 2 A_{k+1 \dots j},k = \frac{i+j}{2} Ak+1j,k=2i+j两个数组,那么我们的目标就是通过求这两个子数组的最大值,然后求得目前这个数组 A i … j A_{i \dots j} Aij的最大子数组和的值。那么问题来了,如果你知道了 A i … k A_{i \dots k} Aik的最大子数组的和 m a x _ l e f t max\_left max_left A k + 1 … j A_{k+1 \dots j} Ak+1</
  • 15
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值