vlambda博客
学习文章列表

算法:最大子数组问题的三个解法


首先补充两个数学公式:

  1. 算法:最大子数组问题的三个解法


什么是最大子数组问题?
给定一数组A, 寻找A中和最大的非空连续子数组。

例如:
输入: A = [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大为 6。

针对该问题,现给出三种解法。


解法一:暴力求解(最直观的方法)
对于长度为 n 的数组A, 任选两个位置作为区间起始和末尾,计算该区间所代表的的子数组的和,选择最大的一个。
符合条件的位置对数有算法:最大子数组问题的三个解法个,因此时间复杂度为算法:最大子数组问题的三个解法.
由于计算区间和时需要借助一个辅助数组,因此空间复杂度为算法:最大子数组问题的三个解法

代码:

int find_max_subArrayvector<int>& A){
    // 构造辅助数组,用来暂存区间和
    vector<int> B = A;
    forint i = 1; i < A.size(); i++)
        B[i] += B[i-1];

    //暴力求解
    int res = INT_MIN;
    forint i = 0; i < A.size(); i++)
        forint 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] 必定为以下三种情况之一:

  1. 最大子数组完全位于 A[low, …, mid] 之间,即 算法:最大子数组问题的三个解法.

  2. 最大子数组完全位于 A[mid, …, high] 之间,即 算法:最大子数组问题的三个解法.

  3. 最大子数组跨越了 mid 位置,即算法:最大子数组问题的三个解法.


三种情况见下图:

算法:最大子数组问题的三个解法

代码:

int find_max_subArrayvector<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];
    forint i = mid - 1; i >= left; i--){
        sum += A[i];
        sum_max = max( sum, sum_max);
    }
    sum = sum_max; // 向另外一侧移动时需要初始化
    forint 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) 

测试用例:

算法:最大子数组问题的三个解法

注:该代码已AC LeetCode 第53题。


时间复杂度为:算法:最大子数组问题的三个解法


解法三:动态规划(不易理解但短小精悍)
初始化变量 sum = 0, 从头遍历数组,对于每一个元素 A[i], 变量 sum += A[i], 一旦 sum < 0, 立即让 sum = 0, 并向后移动一个元素,直到数组尾部。最终选择最大的 sum 值。

为直观起见,以数组 A = [-2, 3, -1, 2, -3] 为例:

算法:最大子数组问题的三个解法

代码:


int find_max_subArrayvector<int>& A){
    int sum = INT_MIN, max_sum = INT_MIN;
    forint i = 0; i < A.size(); i++){
        if( sum < 0)
            sum = 0;
        sum += A[i];
        if( max_sum < sum)
            max_sum = sum;
    }

   return max_sum;
}

测试用例:


注:该代码已AC LeetCode 第53题。


由于只是遍历数组,因此时间复杂度为 .