c++分治法求最大最小值实现_最大子序和

编号:0003
昨天出去玩,少写了一道,今天补上
题目来源:leetcode

题目描述

给定一个整数数组nums,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和

示例

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

题解

自己没想出来,直接看的官方题解

官方题解传送门

官方题解方法有二,动态规划,分治法

动态规划

思路分析

动态规划是将整个数组归纳考虑,假设我们已经知道了以第i-1个数结尾的连续子数组的最大和

,显然以第
i个数结尾的 连续子数组的最大和的可能取值要么为
,要么就
nums[i]单独成一段,也就是 nums[i],在这两个数中我们取最大值,也就是说

有因为显然

,因此我们可以利用这个递推公式,从第一个数开始计算。

因为我们在计算

的时候,只关心
,因此不用把整个
数组保存下来,只需设置一个
pre保存
就好了。

代码

代码如下(我按照官方题解的思路自己写的代码,很麻烦):

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int max, pre;
        max = nums[0];
        pre = nums[0];
        for(int i = 1; i< nums.size(); i++)
        {
            if(pre + nums[i] > nums[i])
                pre = pre+nums[i];
            else
                pre = nums[i];
            if(pre > max)
                max = pre;
        }
        return max;
    }
};

代码如下(官方代码):

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int pre = 0, maxAns = nums[0];
        for (const auto &x: nums) {
            pre = max(pre + x, x);
            maxAns = max(maxAns, pre);
        }
        return maxAns;
    }
};

提交结果

执行用时:8ms,击败74.51%
内存消耗:6.9MB 击败100%

算法复杂度分析

时间复杂度:整个过程中只是对数组进行一遍遍历,每次遍历的时候进行两次 max比较操作,因此时间复杂度

空间复杂度:值设置了两个变量 pre, max因此空间消耗为常数,空间复杂度为

分治法

思路分析

分治法是将问题进行拆分,此题我们处理的问题是从[l,r]区间中找出最大字段和,那么在拆分区间的时候,可以以其中间值mid=(l+r)/2作为分治求解的划分。

对于区间中,我们想要了解的内容有这些

  • lSum表示[l,r]中以l为左端点的最大子段和
  • rSum表示[l,r]中以r为右端点的最大子段和
  • mSum表示[l,r]内的最大子段和
  • iSum表示[l,r]的区间和

首先知道当划分到l==r的问题是可以简单求解的,此时lSum = rSum = mSum = iSum = nums[l]

那么接下来要知道怎么求解合并后的区间的这四个量

首先iSum最好求解,只要把[l,mid]iSum加上[mid,r]iSum就很容易求得

然后当求解rSum的时候,它有两种可能,第一种就是[mid,r]rSum,符合以r为右端点的要求,第二个就是[l,mid]rSum加上[mid,r]iSum,这两段显然是连续的,同时又符合以r为右端点。求其最大值赋给rSum,就好

求解lSum同上,也有两种可能,(1)[l,mid]lSum,(2)[l,mid]iSum加上[mid,r]lSum

求解mSum,若不跨越中间值mid,那么显然其取值就是左区间的mSum和右区间的mSum进行比较。如果跨越了,那就是左子区间的lSum加上右子区间的的rSum,三者取最大

代码

代码如下(官方代码):

class Solution {
public:
    struct Status {
        int lSum, rSum, mSum, iSum;
    };

    Status pushUp(Status l, Status r) {
        int iSum = l.iSum + r.iSum;
        int lSum = max(l.lSum, l.iSum + r.lSum);
        int rSum = max(r.rSum, r.iSum + l.rSum);
        int mSum = max(max(l.mSum, r.mSum), l.rSum + r.lSum);
        return (Status) {lSum, rSum, mSum, iSum};
    };

    Status get(vector<int> &a, int l, int r) {
        if (l == r) return (Status) {a[l], a[l], a[l], a[l]};
        int m = (l + r) >> 1;
        Status lSub = get(a, l, m);
        Status rSub = get(a, m + 1, r);
        return pushUp(lSub, rSub);
    }

    int maxSubArray(vector<int>& nums) {
        return get(nums, 0, nums.size() - 1).mSum;
    }
};

执行结果

执行用时:4ms 击败97.65%
内存消耗:7.1MB 击败100%

复杂度分析

时间复杂度:我们这里相当于对二叉树的所有结点进行遍历,最终遍历时间为
,因此时间复杂度为

空间复杂度:遍历过程中,递归使用
的栈,时间复杂度

暴力法

传送门

思路分析

就是求解出其所有子序然后分别求和,找出其中的最大值就好

代码

class Solution
{
public:
    int maxSubArray(vector<int> &nums)
    {
        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;
    }
};

执行结果

执行用时:600ms 击败5.06%
内存消耗:7.1MB 击败100%

复杂度分析

时间复杂度,整个过程中,每一次对 i的遍历,都进行了
次比较,因此最终的操作次数为
,为

空间复杂度:只定义了两个变量, maxnumsSize,因此空间复杂度为

贪心法

思路分析

这里利用的其实和动态规划有些类似,动态规划比较的是

,可以明显的发现,也就是说只有当
的时候,才采用后者,这里就用
sum存储前面的 nums之和,只有当 sum小于零的时候,才把 sum清零,从下一位置开始进行重新求和相加。

代码如下

class Solution
{
public:
    int maxSubArray(vector<int> &nums)
    {
        //类似寻找最大最小值的题目,初始值一定要定义成理论上的最小最大值
        int result = INT_MIN;
        int numsSize = int(nums.size());
        int sum = 0;
        for (int i = 0; i < numsSize; i++)
        {
            sum += nums[i];
            result = max(result, sum);
            //如果sum < 0,重新开始找子序串
            if (sum < 0)
            {
                sum = 0;
            }
        }

        return result;
    }
};

提交结果

执行用时:8ms,击败74.51%
内存消耗:6.9MB,击败100%

复杂度分析

时间复杂度,整个过程中对数组进行一次遍历,时间复杂度

空间复杂度:只设置了 result,numsSize,sum常数个变量,空间复杂度
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值