day162—递归—买卖股票的最佳时机Ⅱ(LeetCode-122)

题目描述

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。然而,你可以在 同一天 多次买卖该股票,但要确保你持有的股票不超过一股。

返回 你能获得的 最大 利润 。

示例 1:

复制代码
输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3。
最大总利润为 4 + 3 = 7 。

示例 2:

复制代码
输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4。
最大总利润为 4 。

示例 3:

复制代码
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0。

提示:

  • 1 <= prices.length <= 3 * 104
  • 0 <= prices[i] <= 104

解决方案:

这段代码是基于记忆化递归的方式求解 "无限次买卖股票的最佳时机" 问题,在你原有递归思路的基础上,仅做了最小化修改(增加记忆化缓存、规范边界值),既保留了核心递归逻辑,又解决了纯递归的超时和栈溢出问题,能高效处理大数据量用例。

核心逻辑

  1. 核心定义:

    • memo:二维记忆化数组(len×2),memo[end][0/1] 缓存 dfs(end, is) 的结果(0 = 不持有股票,1 = 持有股票),避免重复递归计算;
    • dfs(end, is, nums):返回从第 0 到第end天,在is状态(true= 持有、false= 不持有)下能获得的最大利润。
  2. 递归边界:

    • 当end < 0(所有天数遍历完毕):
      • 若is=true(仍持有股票),返回-0xFFFF(极小值,代表非法状态 ------ 交易结束时持有股票无利润);
      • 若is=false(不持有股票),返回 0(无交易时利润为 0)。
  3. 记忆化优化 :计算dfs(end, is)前,先检查memo[end][state](state为 0/1 对应不持有 / 持有),若不为 - 1 则直接返回缓存值,避免重复递归。

  4. 状态转移(核心逻辑):

    • 持有股票(is=true):利润取 "继续持有(不操作)" 和 "卖出股票(转为不持有,利润 - 当前股价)" 的最大值;
    • 不持有股票(is=false):利润取 "继续不持有(不操作)" 和 "买入股票(转为持有,利润 + 当前股价)" 的最大值;
    • 计算结果存入memo缓存后返回。
  5. 主函数逻辑 :初始化记忆化数组,调用dfs(len-1, false, prices)(从最后一天、不持有股票的初始状态开始计算),返回最终最大利润。

关键特点

  • 最小化修改:完全保留你 "从后往前递归 + 持有 / 不持有状态" 的核心思路,仅增加记忆化和规范边界值;
  • 效率优化:记忆化将时间复杂度从O(2n)降至O(n),解决了大数据量超时 / 栈溢出问题;
  • 逻辑兼容:能正确处理空数组、单调涨跌等边界场景,返回合理的最大利润。

总结

  1. 核心思路:在你原有递归逻辑基础上,通过记忆化缓存避免重复计算,是对纯递归的高效优化;
  2. 关键设计:用二维数组缓存(end, 持有/不持有)状态的结果,是递归优化的核心;
  3. 功能效果:能稳定通过 n=60 甚至更大的股票价格数组用例,返回正确的最大利润。

函数源码:

cpp 复制代码
class Solution {
public:
    
    vector<vector<int>> memo;

    // 仅修改该函数:增加记忆化+修正状态转移符号+规范边界值
    int dfs(int end, bool is, vector<int>& nums) {
        int len = nums.size();
        if(end < 0) {
            // 边界值修正:用INT_MIN表示持有股票的非法状态,更规范
            if(is)  return -0xFFFF;
            return 0;
        }

        int state = is ? 1 : 0; // 0=不持有,1=持有
        if (memo[end][state] != -1) {
            return memo[end][state];
        }

        int res; 
        if(is) {
            
            res = max(dfs(end-1, true, nums), dfs(end-1, false, nums) - nums[end]);
        } else {
            
            res = max(dfs(end-1, false, nums), dfs(end-1, true, nums) + nums[end]);
        }

        // 缓存当前状态的结果
        return memo[end][state] = res;
    }

    int maxProfit(vector<int>& prices) {
        int len = prices.size();
        if (len == 0) return 0; // 空数组边界处理
        
        // 初始化记忆化数组(len行2列,初始值-1表示未计算)
        memo.assign(len, vector<int>(2, -1));
        
        // max(0, ...) 确保利润不会为负(无交易时利润为0)
        return dfs(len-1, false, prices);   
    }
};
相关推荐
Interview Aid11233 分钟前
Stripe VO 面经汇总:5 轮面试题目与考察重点
算法
EEET智选38 分钟前
科技晨间速报 | AI 安全警钟、开源新模型与苹果智能家居
人工智能·科技·算法·机器学习·ai
挖掘狂人1 小时前
哈希算法:一根看不见的 "指纹",如何撑起整个互联网
算法·程序员·掘金技术征文
鶴哥只手遮天1 小时前
ecRad 1.5.0 算法原理详解
算法·开源
不会就选b1 小时前
算法日常・每日刷题--<动态规划>22
算法·动态规划
释厄6231 小时前
字画同体论——字即画·几何即字·漫画与小说=文本层
人工智能·算法
沫璃染墨2 小时前
从零入门数据结构与算法(一):复杂度分析——从算法效率到时间与空间复杂度
c语言·开发语言·数据结构·笔记·算法·飞书
熊猫钓鱼>_>2 小时前
面试出现 606 次的第 K 大,被拆成三个问题问
算法·面试·职场和发展·校招·面经·oj·ai生成
xuxigifxfh3 小时前
牛客:BISHI2 【模板】栈的操作
java·开发语言·windows·算法
INGNIGHT3 小时前
593 · 石头游戏 II (区间dp石子合并 翻倍循环问题)
leetcode