剑指 Offer 42. 连续子数组的最大和

剑指 Offer 42. 连续子数组的最大和


题目链接: 剑指 Offer 42. 连续子数组的最大和

有关题目

输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。
求所有子数组的和的最大值。

要求时间复杂度为O(n)。
示例1:

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

1 <= arr.length <= 10^5
-100 <= arr[i] <= 100

题解

法一:暴力法

class Solution
{
public:
    int maxSubArray(vector<int> &nums)
    {
        //类似寻找最大最小值的题目,初始值一定要定义成理论上的最小最大值
        //考察为以第i个元素起始的连续子数组的最大和
        int max = INT_MIN;
        int numsSize = int(nums.size());
        for (int i = 0; i < numsSize; i++)
        {
            int sum = 0;
            for (int j = i; j < numsSize; j++)
            {
                sum += nums[j];
                if (sum > max)
                {
                    max = sum;
                }
            }
        }
        return max;
    }
};

法二:动态规划

思路:
动态规划求出每个位置的pre[i],返回数组pre中的最大值就好

int maxSubArray(int* nums, int numsSize){
    int pre[numsSize];//pre[i]表示nums中以nums[i]结尾的最大子序和
    int res = nums[0];

    //类似寻找最大最小值的题目,初始值一定要定义成理论上的最小最大值
    for (int i = 0; i < numsSize; i++)
    {
        pre[i] = -100;//题干给的条件,pre元素全部初始化最小值
    }

    pre[0] = nums[0];//初始化
    for (int i = 1; i < numsSize; i++)
    {
        pre[i] = fmax(pre[i - 1] + nums[i], nums[i]);//动态转移方程
        res = fmax(res,pre[i]);//由于我们定义的动态转移的意义为以nums[i]结尾的最大子序和
        //我们添加一个变量来记录最大值
    }
    return res;
}

法三:滚动数组

int maxSubArray(int* nums, int numsSize){
    int pre = 0, maxAns = nums[0];
    for (int i = 0; i < numsSize; ++i)
    {
        pre = fmax(pre + nums[i], nums[i]);//前第i - 1项中的最大值加上当前项 与 第i项比较

        maxAns = fmax(pre, maxAns);//存储出现的最大值
    }
    return maxAns;
}

法四:贪心

思路:
我们''贪心''的关键点在sum是否小于0
int maxSubArray(int* nums, int numsSize){
    int maxAns = nums[0];
    int sum = 0;
    for (int i = 0; i < numsSize; i++)
    {
        sum += nums[i];
        maxAns = fmax(maxAns,sum);
        if (sum < 0) sum = 0;
    }
    return maxAns;
}

法五:分治

//对于一个区间[l,r],我们可以维护四个量:
//lSum表示[l,r]内以 l 为左端点的最大子段和
//rSum 表示 [l,r] 内以 r 为右端点的最大子段和
//mSum 表示 [l,r] 内的最大子段和
//iSum 表示 [l,r] 的区间和
struct Status {
    int lSum, rSum, mSum, iSum;
};

//如何维护lSum, rSum, mSum, iSum
struct Status pushUp(struct Status l, struct Status r){
    int iSum = l.iSum + r.iSum;
    int lSum = fmax(l.lSum, l.iSum + r.lSum);
    int rSum = fmax(r.rSum, r.iSum + l.rSum);
    int mSum = fmax(fmax(l.mSum, r.mSum), l.rSum + r.lSum);
    return (struct Status){lSum, rSum, mSum, iSum};
}

//此函数用于在数组a中寻找以[l,r]为区间的最大子序和
struct Status get(int* a, int l, int r){
     if (l == r) {
         //递归逐层深入直到区间长度缩小为 1 的时候,则lSum, rSum, mSum, iSum都相等
         //即递归结束条件
        return (struct Status){a[l], a[l], a[l], a[l]};
    }

    //我们对[l,r]实行分治操作,分为[l,m] 与[m + 1,r](其中m = (l + r) / 2)
    int m = (l + r) >> 1;
    struct Status lSub = get(a, l, m);
    struct Status rSub = get(a, m + 1, r);
    return pushUp(lSub, rSub);
}

int maxSubArray(int* nums, int numsSize) {
    return get(nums, 0, numsSize - 1).mSum;
}

剑指 Offer 42. 连续子数组的最大和

上一篇:剑指 Offer 42. 连续子数组的最大和 力扣(简单) 一直困扰的dp


下一篇:剑指offer 42 连续子数组的最大和