当前位置: 首页 > article >正文

【每日一题】最大子数组和

文章目录

  • Tag
  • 题目来源
  • 题目解读
  • 解题思路
    • 方法一:动态规划
    • 方法二:分治
    • 方法三:前缀和
  • 写在最后

Tag

【动态规划】【前缀和】【数组】【2023-11-20】


题目来源

53. 最大子数组和


题目解读

找出数组 nums 中连续子数组元素和的最大值。数组中的元素范围为 [ − 1 0 4 , 1 0 4 ] [-10^4, 10^4] [104,104],数组长度最大为 1 0 5 10^5 105

进阶:如果你已经成功实现了时间复杂度为 O ( n ) O(n) O(n) 的解法,尝试使用更为精妙的 分治法 求解。


解题思路

方法一:动态规划

状态

dp[i] 表示以第 i 个数结尾的连续子数组的最大和,最后返回的结果为:

m a x 0 < = i < = n − 1 d p [ i ] max_{0<=i<=n-1}{dp[i]} max0<=i<=n1dp[i]

转移关系

以第 i 个数结尾的连续子数组的最大和有这样的转移关系:

d p [ i ] = m a x ( d p [ i − 1 ] + n u m s [ i ] , n u m s [ i ] ) dp[i] = max(dp[i-1] + nums[i], nums[i]) dp[i]=max(dp[i1]+nums[i],nums[i])

base case

由于以第 i 个数结尾的连续子数组的最大和只和上一个状态有关,因此可以使用一个变量 prev 来维护上一个转态的最大子数组和,这样就可以将时间复杂度降低到 O(1)

初始化 prev = 0res = nums[0](表示本次状态的最大值)。

最后返回

最后返回 res

实现代码

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int prev = 0, res = nums[0];
        for (int num : nums) {
            prev = max(prev + num, num);
            res = max(res, prev);
        }
        return res;
    }
};

复杂度分析

时间复杂度: O ( n ) O(n) O(n)

空间复杂度: O ( 1 ) O(1) O(1)


方法二:分治

方法二的计算思想类似于线段树,实质上是分治思想的应用。在线段树中我们可以维护一个区间上的最大元素值、最小元素值以及元素和。本题中使用分治思想解决,我们首先需要解决维护区间上什么样的数据结构。

对于一个区间 [l, r],我们维护四个变量:

  • lSum:表示 [l, r] 内以 l 为左端点的最大子数组和;
  • rSum:表示 [l, r] 内以 r 为右端点的最大子数组和;
  • mSum:表示 [l, r] 内最大子数组和;
  • iSum:表示 [l, r] 的区间和。

区间 [l, r] 上四个变量如何更新呢?

  • iSum 等于左子区间的 lSum 加上右子区间的 rSum,即 iSum = lSum + rSum
  • 区间 [l, r] 上的 lSum 要么等于左子区间 [l, m]lSum,要么等于左子区间 [l, m]iSum 加上右子区间 lSum,二者取较大值;
  • 同理,区间 [l, r] 上的 rSum 要么等于右子区间 [m+1, r]rSum,要么等于右子区间 [m+1, r]rSum 加上左子区间 rSum,二者取较大值;
  • 当计算好上面的三个量之后,就很好计算 [l,r]mSum 了。我们可以考虑 [l,r]mSum 对应的区间是否跨越 m——它可能不跨越 m,也就是说 [l,r]mSum 可能是「左子区间」的 mSum 和 「右子区间」的 mSum 中的一个;它也可能跨越 m,可能是「左子区间」的 rSum 和 「右子区间」的 lSum 求和。三者取最大值。

实现代码

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;
    }
};

复杂度分析

时间复杂度:渐进的时间复杂度为 O ( n ) O(n) O(n)

空间复杂度:递归会使用栈空间,空间复杂度为 O ( l o g n ) O(logn) O(logn)

方法三:前缀和

还可以使用前缀和的方法来解决。

我们在遍历数组 nums 时,设当前遍历的元素为 num

  • 更新前缀和 preSum += num
  • 最大子数组和等于当前前缀和减去上次更新的最小前缀和,即 res = max(res, preSum - minPreSum)
  • 更新最小前缀和 minPreSum = min(minPreSum, preSum)

实现代码

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int preSum = 0, minPreSum = 0;
        int res = INT_MIN;
        for (int num : nums) {
            preSum += num;
            res = max(res, preSum - minPreSum);
            minPreSum = min(minPreSum, preSum);
        }
        return res;
    }
};

复杂度分析

时间复杂度: O ( n ) O(n) O(n)

空间复杂度: O ( 1 ) O(1) O(1)


写在最后

如果文章内容有任何错误或者您对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。

如果大家有更优的时间、空间复杂度方法,欢迎评论区交流。

最后,感谢您的阅读,如果感到有所收获的话可以给博主点一个 👍 哦。


http://www.kler.cn/a/134792.html

相关文章:

  • SHA-256哈希函数
  • Oracle ADB 导入 BANK_GRAPH 的学习数据
  • #include<string>和#include<string.h>有什么区别
  • Qwen2-VL:发票数据提取、视频聊天和使用 PDF 的多模态 RAG 的实践指南
  • apache2配置多站点
  • 曹操为什么总是亲征
  • 小程序商城免费搭建之java商城 电子商务Spring Cloud+Spring Boot+二次开发+mybatis+MQ+VR全景+b2b2c
  • 越南MIC新规针对ICT和ITE产品电气授权标准变更
  • 一起学docker系列之四docker的常用命令--系统操作docker命令及镜像命令
  • Springcloud可视化物联网智慧工地云SaaS平台源码 支持二开和私有化部署
  • 沸点 | Ultipa 图数据库金融应用场景优秀案例首批入选,金融街论坛年会发布
  • Chat GPT 用于论文润色,常用指令这里都全了
  • ts视频文件转为mp4(FFmpeg)
  • 『亚马逊云科技产品测评』活动征文|基于next.js搭建一个企业官网
  • 每天一道算法题(五)——判断一组数字是否连续,出现连续数字的时候以‘-’输出
  • Flutter笔记:目录与文件存储以及在Flutter中的使用(上)
  • Git 提交竟然还能这么用?
  • css设置下划线
  • MCU内存基础知识
  • 下载node-sass
  • Vue 3.0 中重置 reactive 定义的响应式对象数据,恢复为初始值
  • grafana面板介绍
  • 深入分析高性能互连点对点通信开销
  • 搭建 AI 图像生成器 (SAAS) php laravel
  • 详解使用asyncio实现playwright并发操作(复制源码即可运行)
  • [Kettle] 生成记录