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

news/2024/7/7 20:20:13

文章目录

  • 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://lihuaxi.xjx100.cn/news/1810772.html

相关文章

【canvas】了解canvas,并实现会议预定记录钟表盘、页面水印

初识canvas Canvas 有什么用 Canvas 允许使用直线、曲线、矩形、圆形等基本图形绘制出复杂的图形 Canvas 可以加载图像&#xff0c;并进行各种处理&#xff0c;如裁剪、缩放、旋转等操作 Canvas 可以通过 JavaScript 控制&#xff0c;所以你可以利用帧动画原理&#xff0c;…

vue-admin-template改变接口地址

修改登录接口 1.f12查看请求接口 模仿返回数据写接口 修改方式1 1.在env.devolopment修改 修改方式2 vue.config.js 改成本地接口地址 配置转发 后端创建相应接口&#xff0c;使用map返回相同的数据 修改前端请求路径 修改前端返回状态码 utils里面的request.js

关于sqlModel 实现查询表单入参空值和模糊匹配一次性查询

在处理表单提交后&#xff0c;后端 SQL 查询部分空值和部分模糊值时&#xff0c;可以使用 SQLModel 构建动态查询。你可以根据表单数据动态构建 SQL 查询&#xff0c;并且只添加那些非空的、有值的条件。 以下是一个示例&#xff0c;假设你有一个模型 Item&#xff1a; from …

软件测试面试时问你的项目经验,你知道该怎么说吗?

很简单&#xff0c;我来给你们一个公式 0 自我介绍&#xff0c;名字 学历 荣誉。 1 简述项目背景&#xff0c;你身处这个项目是做什么的。 不要太细&#xff0c;试着引导一下面试官让他提问。这样&#xff0c;请问您对此有什么疑问吗&#xff1f; 2 简述 你在项目中的角色&…

《算法通关村——反转字符串中的单词问题解析》

《算法通关村——反转字符串中的单词问题解析》 151. 反转字符串中的单词 给你一个字符串 s &#xff0c;请你反转字符串中 单词 的顺序。 单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。 返回 单词 顺序颠倒且 单词 之间用单个空格连接…

ckplayer自己定义风格播放器的开发记录

CKplayer是一款基于Flash和HTML5技术的开源视频播放器&#xff0c;支持多种格式的音视频播放&#xff0c;并且具有优秀的兼容性和扩展性。 它不仅可以在网页上播放本地或者网络上的视频&#xff0c;还可以通过代码嵌入到网页中&#xff0c;实现更加个性化的播放效果。CKplayer…

【Reading Notes】

文章目录 中文AA 或 AAAAAAAAAAA&#xff0c;BBBBAAAA&#xff0c;BBBB&#xff0c;CCCCAAAA&#xff0c;BBBB&#xff0c;CCCC&#xff0c;DDDDAAAAAAAAAA&#xff0c;BBBBBAAAAA&#xff0c;BBBBB&#xff0c;CCCCC&#xff08;肆&#xff09;AAAAA&#xff0c;BBBBB&#xf…

Java JSON字符串替换其中对应的值

代码&#xff1a; public static void main(String[] args) { // String theData crmScene.getData();String theData "[{\"type\":1,\"values\":[\"审批中\",\"未交付\"],\"name\":\"status\"}]"…