从股市走势到动态规划:探索最大子数组和问题

生活中的算法

想象你是一位股票交易员,手上有一支股票的每日涨跌数据。你想找出哪段连续的交易日能获得最大的收益。如果某天股票上涨5元,我们记为+5,下跌3元记为-3。找出总和最大的一段连续交易日,就是在寻找最大子数组和。

这个问题在现实生活中很常见。比如分析用户活跃度的波动趋势,研究气温变化的最大累积效应,或是评估企业连续几个月的盈利表现。

问题描述

LeetCode第53题"最大子数组和"是这样描述的:给你一个整数数组 nums,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

例如:

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

最直观的解法:暴力枚举

最容易想到的方法是:枚举所有可能的子数组,计算它们的和,找出最大值。

让我们用一个简单的例子来理解:

nums = [1,-2,3,-1]
检查子数组 [1]:和为1
检查子数组 [1,-2]:和为-1
检查子数组 [1,-2,3]:和为2
检查子数组 [1,-2,3,-1]:和为1
检查子数组 [-2]:和为-2
...依此类推
找到最大和为3,对应子数组[3]

优化解法:动态规划

仔细思考会发现,我们在计算每个位置结尾的最大子数组和时,只需要关注前一个位置的最大子数组和是否值得接续。

类似继承资产,如果之前继承的是正资产,不管多还是少,有总比没有好。我在之前的资产基础上,加上我目前的资产或者负债。

可要是之前继承的是一笔负债,那我宁可不要,选择白手起家。

动态规划的原理

  1. 定义dp[i]为以第i个数结尾的最大子数组和
  2. 如果前面的和是正数,就值得接续;如果是负数,就重新开始
  3. 状态转移方程:dp[i] = max(dp[i-1] + nums[i], nums[i])
  4. 最终答案是dp数组中的最大值

算法步骤演示

用nums = [1,-2,3,-1]演示这个过程:

1. 处理1:
dp[0] = 1
当前最大和 = 1 2. 处理-2:
dp[1] = max(1-2, -2) = -1
当前最大和 = 1 3. 处理3:
dp[2] = max(-1+3, 3) = 3
当前最大和 = 3 4. 处理-1:
dp[3] = max(3-1, -1) = 2
最终最大和 = 3

Java代码实现

public int maxSubArray(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
} // dp[i]表示以i结尾的最大子数组和
int[] dp = new int[nums.length];
dp[0] = nums[0];
int maxSum = dp[0]; for (int i = 1; i < nums.length; i++) {
// 状态转移:要么接续前面的和,要么重新开始
dp[i] = Math.max(dp[i-1] + nums[i], nums[i]);
// 更新全局最大和
maxSum = Math.max(maxSum, dp[i]);
} return maxSum;
}

进一步优化:空间优化

观察发现,我们其实只需要前一个状态的值,不需要保存整个dp数组。

public int maxSubArray(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
} int currentMax = nums[0];
int maxSum = nums[0]; for (int i = 1; i < nums.length; i++) {
currentMax = Math.max(currentMax + nums[i], nums[i]);
maxSum = Math.max(maxSum, currentMax);
} return maxSum;
}

解法比较

让我们比较这些解法:

暴力枚举:

  • 时间复杂度:O(n²)
  • 空间复杂度:O(1)
  • 优点:直观易懂
  • 缺点:效率较低

动态规划:

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)
  • 优点:高效且易于理解
  • 缺点:需要额外空间

空间优化版:

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
  • 优点:时空效率都很高
  • 缺点:代码不如dp数组版直观

扩展思考

这道题目启发我们:

  1. 当遇到"最大"/"最小"类型的问题时,考虑动态规划
  2. 寻找问题中的递推关系
  3. 关注是否可以优化空间复杂度
  4. 注意处理负数的情况

类似的问题还有:

  • 买卖股票的最佳时机
  • 乘积最大子数组
  • 环形子数组的最大和

小结

通过最大子数组和这道题,我们不仅学会了一个经典的动态规划问题的解法,更重要的是理解了如何将复杂问题分解为子问题,并利用子问题的解构建最终答案。这种思维方式在解决其他动态规划问题时也会很有帮助。

记住,当遇到需要求解"最大连续和"类型的问题时,动态规划往往能提供一个优雅而高效的解决方案!


作者:忍者算法

公众号:忍者算法

我准备了一份刷题清单,以及这些题目的详细题解,覆盖了绝大部分常见面试题。我可以很负责任地说,只要你把这些题真正掌握了,80%的算法面试都能遇到相似题目。公众号回复【刷题清单】获取~

【忍者算法】从股市走势到动态规划:探索最大子数组和问题|LeetCode 53 最大子数组和的更多相关文章

  1. LeetCode算法训练-贪心算法 455.分发饼干 376. 摆动序列 53. 最大子序和

    欢迎关注个人公众号:爱喝可可牛奶 LeetCode算法训练-贪心算法 455.分发饼干 376. 摆动序列 53. 最大子序和 前置知识 贪心算法核心是找局部最优解,通过局部最优推导出全局最优 Lee ...

  2. python常用算法(7)——动态规划,回溯法

    引言:从斐波那契数列看动态规划 斐波那契数列:Fn = Fn-1 + Fn-2    ( n = 1,2     fib(1) = fib(2) = 1) 练习:使用递归和非递归的方法来求解斐波那契数 ...

  3. 算法导论——lec 11 动态规划及应用

    和分治法一样,动态规划也是通过组合子问题的解而解决整个问题的.分治法是指将问题划分为一个一个独立的子问题,递归地求解各个子问题然后合并子问题的解而得到原问题的解.与此不同,动态规划适用于子问题不是相互 ...

  4. 五大常用算法之二:动态规划算法(DP)

    一.基本概念 动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移.一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划. 二.基本思想与策略 基本 ...

  5. 小旭讲解 LeetCode 53. Maximum Subarray 动态规划 分治策略

    原题 Given an integer array nums, find the contiguous subarray (containing at least one number) which ...

  6. 前端与算法 leetcode 26. 删除排序数组中的重复项

    目录 # 前端与算法 leetcode 26. 删除排序数组中的重复项 题目描述 概要 提示 解析 算法 # 前端与算法 leetcode 26. 删除排序数组中的重复项 题目描述 26. 删除排序数 ...

  7. 前端与算法 leetcode 350. 两个数组的交集 II

    目录 # 前端与算法 leetcode 350. 两个数组的交集 II 题目描述 概要 提示 解析 解法一:哈希表 解法二:双指针 解法三:暴力法 算法 # 前端与算法 leetcode 350. 两 ...

  8. Leetcode之动态规划(DP)专题-53. 最大子序和(Maximum Subarray)

    Leetcode之动态规划(DP)专题-53. 最大子序和(Maximum Subarray) 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和. ...

  9. leetcode 刷题(数组篇)152题 乘积最大子数组 (动态规划)

    题目描述 给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积. 示例 1: 输入: [2,3,-2,4] 输出: 6 解释: 子 ...

  10. LeetCode初级算法的Python实现--动态规划

    动态规划的本质是递归:所以做题之前一定要会递归:递归式就是状态转移方程:这里将会介绍使用动态规划做题的思维方式. 统一的做题步骤: 1.用递归的方式写出代码:(此方法写的代码在leetcode中一定会 ...

随机推荐

  1. manim边做边学--多面体

    在Manim中,对于多面体,有一系列封装好的类可以直接使用. 使用它们,可以方便快速的构建正多面体: Polyhedron:通过顶点和面的参数构建任意多面体 Tetrahedron:四面体 Octah ...

  2. [Java] Stream流使用最多的方式

    Java 中 Stream 流的用法全解析 在 Java 编程中,Stream 流提供了一种高效.便捷的方式来处理集合数据.它可以让我们以声明式的方式对数据进行各种操作,如过滤.映射.排序.聚合等,大 ...

  3. 07C++选择结构(1)

    一.基础知识 1.关系运算符 因为我们要对条件进行判断,必然会用到关系运算符: 名称 大于 大于等于 小于 小于等于 等于 不等于 符号 > >= < <= == != 关系表 ...

  4. flutter问题汇总

    Text文字居中 Text(           'You will need to post a photo before you can play!',           textAlign:  ...

  5. 介绍 MSTest Runner - CLI, Visual Studio 等更多

    介绍 MSTest Runner - CLI, Visual Studio 等更多 https://devblogs.microsoft.com/dotnet/introducing-ms-test- ...

  6. HTMLDivElement.prototype

    HTMLDivElement.prototype.test = function(){console.log("i am div")} $0.innerHTML = '<di ...

  7. simpleui

    目录 一.simpleui 1.1 使用步骤 1.2 功能介绍 1.3 展示大屏 一.simpleui 之前公司里,做项目前后端结合,要使用权限,要快速搭建后台管理,使用djagno的admin直接搭 ...

  8. 如何调整Gitlab-Runner最大并发数?

    概述: 我们在使用gitlab-runner做cicd时,如果安装之后没有配置gitlab-runner的最大并发数,在使用时候可能会碰到job的警告(job日志超过字节限制):job's log e ...

  9. mysql转换类型、类型转换、查询结果类型转换

    一.问题来源 数据库一张表的主键id设为了自增,那就是int型的,但是其他表的关联字段又设置成了字符串! 而且已经开发了很久才发现问题,既然出现了问题肯定需要解决 如图 很明显id是不一样的,花了点时 ...

  10. 禁止所有搜索爬虫访问网站指定目录robots.txt

    禁止所有搜索爬虫访问网站指定目录 用自己私人的服务器来测试吧,99买阿里云 文件放置不让爬取的网站文件夹目录里 robots.txt User-agent: * Disallow: / User-Ag ...