连续子数组的最大和

给出一个整数序列S,其中有N个数,定义其中一个非空连续子序列T中所有数的和为T的“序列和”。 对于S的所有非空连续子序列T,求最大的序列和。

至少有以下三种解法:

1. 动态规划

  • 时间复杂度O(n)
void max_subarray_sum(const std::vector<int>& a, std::ostream& os)
{
    int res = INT_MIN;
    int cur_sum = 0;
    for (int i = 0; i < a.size(); i++)
    {
        cur_sum = std::max(cur_sum + a[i], a[i]);
        res = std::max(res, cur_sum);
    }
    os << res;
}

此种解法可以理解为,假设有最大序列和的子数组是以原数组中第i个元素结尾的,那么在遍历的过程中做两件事:

  1. 对于每个i,找到以第i个元素结尾的子数组的最大序列和
  2. 对于上述找到的最大序列和,比较得出其中最大的即为最终结果
    代码中的cur_sum就是为了做第1件事,而res是为了做第2件事。得到cur_sum的逻辑为,以第i个元素结尾的序列和最大的子数组,等于以第(i-1)个元素结尾的序列和最大的子数组加上第i个元素,或者等于第i个元素

2. 扫描法

  • 时间复杂度O(n)
void max_subarray_sum(const std::vector<int> &a, std::ostream &os)
{
    int res = INT_MIN;
    int cur_sum = INT_MIN;
    for (int i = 0; i < a.size(); i++)
    {
        if (cur_sum < 0)
        {
            cur_sum = a[i];
        }
        else
        {
            cur_sum += a[i];
        }
        if(res < cur_sum)
        {
            res = cur_sum;
        }
    }
    os << res;
}

这种扫描法其实和动态规划是一样的,因为,如果cur_sum < 0那么cur_sum + a[i] < a[i]否则cur_sum + a[i] >= a[i]。同时,这也提供了另一种理解思路,在扫描的过程中逐步求序列和,当求得的和为负时,将原有的和丢弃,开始重新计算,因为负的和与后面的值相加,只能使后面的值减小。

3. 分治法

  • 时间复杂度O(nlogn)
int max_subarray_sum_helper(const std::vector<int>& a, int left, int right)
{
    if (abs(left - right) < 2)
    {
        return a[left] > a[right] ? a[left] : a[right];
    }
    int middle = (left + right) / 2;
    int left_max = max_subarray_sum_helper(a, left, middle);
    int right_max = max_subarray_sum_helper(a, middle + 1, right); // (middle + 1) instead of middle 
    LOG(WARNING) << left << " " << middle << " " << right;

    int middle_max = INT_MIN;
    int cur_sum = 0;
    for (int i = middle; i >= left; i--)
    {
        cur_sum += a[i];
        middle_max = cur_sum > middle_max ? cur_sum : middle_max;
    }
    cur_sum = middle_max; // cur_sum should be set to the left_max_sum
    for (int i = middle + 1; i <= right; i++)
    {
        cur_sum += a[i];
        middle_max = cur_sum > middle_max ? cur_sum : middle_max;
    }
    int maxsum = left_max > right_max ? left_max : right_max;
    maxsum = maxsum > middle_max ? maxsum : middle_max;
    LOG(INFO) << "left: " << left << " right: " << right << " max_sum: " << maxsum;
    return maxsum;
}

void max_subarray_sum(const std::vector<int> &a, std::ostream &os)
{
    os << max_subarray_sum_helper(a, 0, a.size() - 1);
}

这里的分治法的基本思想就是将数组分为两部分,最大序列和即为左边数组的最大序列和,或者右边数组的最大序列和,或者是位于中间跨越左右两个数组的最大序列和

4.参考文献

[1] 求连续子数组的最大和
https://www.cnblogs.com/waytofall/archive/2012/04/10/2439820.html
[2] [LeetCode] Maximum Subarray 最大子数组
https://www.cnblogs.com/grandyang/p/4377150.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值