力扣解题-122. 买卖股票的最佳时机 II

3 阅读6分钟

力扣解题-122. 买卖股票的最佳时机 II

给你一个整数数组 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


第一次解答

解题思路

核心方法:贪心策略 + 高低点捕捉(一次遍历),通过维护“当前持仓成本(买入价)”,遍历过程中捕捉每一段上涨的利润,只要后一天价格高于当前买入价就卖出获利,并将买入价更新为当天价格(等效于“当天卖当天买”),时间复杂度O(n)、空间复杂度O(1),是适配题目规则的高效解法。

核心原理铺垫

题目允许“当天买当天卖、多次买卖”,核心逻辑是:所有上涨阶段的利润之和就是最大利润。例如价格序列[1,5,3,6],上涨段为1→5(利润4)、3→6(利润3),总利润7,与“1买6卖”的利润相同,但拆分后更易通过遍历实现:

  • left:记录当前持仓的买入价,初始为第一天价格(prices[0]);
  • max:累计所有上涨段的利润,初始为0;
  • right:遍历指针,从第二天开始检查价格变化,决定是否卖出获利。
具体步骤
  1. 初始化核心变量
    • left = prices[0]:初始买入价为第一天的价格;
    • max = 0:初始总利润为0(无盈利时返回0)。
  2. 遍历数组捕捉利润
    • 遍历指针right从1开始遍历数组(第二天及以后);
    • 若当前价格prices[right] > left(当前卖出可获利):
      • 计算单次利润temp = prices[right] - left
      • 将单次利润累加到max,并将left更新为prices[right](等效于卖出后当天再买入,继续捕捉后续上涨);
    • 若当前价格prices[right] <= left(当前价格更低,更适合买入):
      • 更新leftprices[right](更换更低的买入价,等待后续上涨);
  3. 返回结果:遍历完成后返回累计的总利润max
核心优化逻辑说明
  1. 时间复杂度最优:仅一次遍历数组(O(n)),每个元素仅被访问一次,无嵌套循环,适配题目3×10⁴级别的数据规模;
  2. 空间复杂度极致:仅使用三个基础变量,空间复杂度O(1),无额外内存开销;
  3. 规则适配性:利用“当天买卖”的规则,将“持有到最高点卖出”拆分为“每一段上涨都卖出”,最终利润等价但逻辑更简单,无需预判后续价格;
  4. 逻辑等价性:该思路与“持有到连续上涨的终点卖出”完全等价,例如[1,2,3,4]中,“1买4卖”利润3,与“1买2卖+2买3卖+3买4卖”利润之和(1+1+1=3)完全一致。
    public int maxProfit(int[] prices) {
        int left=prices[0];
        int max=0;
        for(int right=1;right<prices.length;right++){
            if(prices[right]>left){
                int temp=prices[right]-left;
                left=prices[right];
                max+=temp;
            }else {
                left=prices[right];
            }
        }
        return max;
    }

示例解答

解题思路

核心方法:贪心策略 + 每日利润累加(一次遍历),将“多次买卖的总利润”拆解为“所有相邻两天的正利润之和”,直接累加每天的上涨收益,是本题最简洁的最优解法,时间复杂度O(n)、空间复杂度O(1)。

核心原理铺垫

该思路是对“捕捉所有上涨段利润”的极致简化:

  • 若第i天价格 > 第i-1天价格,说明这两天存在上涨利润(prices[i] - prices[i-1]),将该利润累加;
  • 若第i天价格 ≤ 第i-1天价格,说明无利润,累加0(不影响总利润);
  • 最终累加的总和就是所有上涨段的利润之和,与“低买高卖多次交易”的总利润完全一致。
具体步骤
  1. 初始化结果变量ans = 0,用于累计所有正利润;
  2. 遍历数组累加正利润
    • 从第2天(索引1)开始遍历数组;
    • 计算当天与前一天的价格差prices[i] - prices[i-1]
    • Math.max(0, 价格差)筛选出正利润,累加到ans中(负利润则累加0);
  3. 返回结果:遍历完成后返回ans,即最大总利润。
核心优势说明
  1. 逻辑极简:无需维护买入价,仅通过相邻价格差判断,代码行数最少、可读性最高;
  2. 性能最优:一次遍历+简单算术运算,时间复杂度O(n),常数因子极小,是本题的最优写法;
  3. 结果等价性:例如示例1的价格序列[7,1,5,3,6,4],相邻差价为[-6,4,-2,3,-2],正利润累加4+3=7,与实际交易利润完全一致;
  4. 边界处理天然适配:当价格持续下跌时,所有差价均为负,累加结果为0,符合“不交易利润最大”的规则。
public int maxProfit(int[] prices) {
    int ans = 0;
    for (int i = 1; i < prices.length; i++) {
        ans += Math.max(0, prices[i] - prices[i - 1]);
    }
    return ans;
}

总结

  1. 第一次解答(高低点捕捉):通过维护买入价捕捉每一段上涨利润,逻辑直观,易理解“低买高卖”的交易过程;
  2. 示例解答(每日利润累加):对贪心策略的极致简化,无需维护买入价,仅累加相邻正差价,代码最简洁、性能最优;
  3. 核心共性:两种解法均基于“贪心策略”,核心是捕捉所有上涨段的利润,时间复杂度O(n)、空间复杂度O(1),是本题的最优解;
  4. 关键结论:在允许多次买卖的规则下,最大利润等于所有相邻上涨日的利润之和,无需预判长期趋势,逐段获利即可。