力扣算法 121. 买卖股票的最佳时机

力扣算法 121. 买卖股票的最佳时机

1. 题目描述

  • 题目:

    给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

    你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

    返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0

  • 输入: 整数数组 pricesprices[i] 表示第 i 天的股票价格

  • 输出: 你最多完成 一次 交易(买入一次 + 卖出一次)能获得的最大利润

  • 要求:

    • 必须先买后卖(卖出日要在买入日之后)

    • 如果不能获得利润,返回 0

  • 题目关键点:

    1. 只能交易一次(不是多次买卖)

    2. 卖出必须发生在买入之后(时间顺序约束)

    3. 需要最大化 prices[j] - prices[i]j > i

示例

输入:[7,1,5,3,6,4]

输出:5

解释:第 2 天买入(1),第 5 天卖出(6),利润 6 - 1 = 5


2. 解题思路(先讲人话,再讲算法)

我把题目翻译成大白话就是:

我只能选一个"最低的买入价"(且在前面出现),再在它后面找一个"最高的卖出价",让差值最大。

然后我们要做到:

  1. 从左到右遍历时,随时记住到目前为止最低价格 minPrice(最佳买入点候选)

  2. 每到一天价格 price,就尝试把它当成"卖出价",更新最大利润:maxProfit = max(maxProfit, price - minPrice)


3. 题解(核心算法)

3.1 为什么用这个方法(为什么不用别的)

  • 暴力法 会枚举所有买入日 i 和卖出日 jj>i),时间复杂度 O(n^2)n 最大可到 10^5,会直接超时。

  • 贪心/一次遍历 的关键观察:

    当我们遍历到第 i 天时,若想在这一天卖出,最佳买入一定是 之前出现过的最低价

    所以只要维护一个 minPrice,就能在 O(n) 内完成。

这题也可以用 DP 理解,但实现上就是"维护历史最低价 + 维护最大差值"。

3.2 关键变量定义(读代码前先认清角色)

  • minPrice:从第 0 天到当前天为止出现过的最低价格(最佳买入候选)

  • maxProfit:目前为止能得到的最大利润

  • price:当天价格(遍历时的当前值)

3.3 代码(完整可运行,Java)

java 复制代码
class Solution {
    public int maxProfit(int[] prices) {
        // 边界:题目保证 length >= 1,但写上更稳
        if (prices == null || prices.length == 0) return 0;

        int minPrice = prices[0];  // 历史最低价(买入价)
        int maxProfit = 0;         // 最大利润(不亏就至少为 0)

        for (int i = 1; i < prices.length; i++) {
            int price = prices[i];

            // 1) 尝试今天卖出:利润 = 今天价格 - 历史最低价
            maxProfit = Math.max(maxProfit, price - minPrice);

            // 2) 更新历史最低价:为后续卖出准备更低的买入点
            minPrice = Math.min(minPrice, price);
        }

        return maxProfit;
    }
}

4. 代码逐段/逐行讲解(最重要)

4.1 初始化部分

  • int minPrice = prices[0];

    第一天如果买入,那买入价就是 prices[0]。我们先把它当作当前最低价。

  • int maxProfit = 0;

    因为允许"不交易",且不能亏钱,所以最大利润最小为 0。

4.2 主循环部分

  • for (int i = 1; i < prices.length; i++)

    从第二天开始遍历,因为第一天已经用于初始化最低价。

  • maxProfit = Math.max(maxProfit, price - minPrice);
    把今天当作卖出日 :如果之前最低价买入,今天卖出能赚多少?

    用它尝试更新最大利润。

  • minPrice = Math.min(minPrice, price);
    更新最低价:今天价格如果更低,就可能成为未来更好的买入点。

记忆口诀:
先算"今天卖出能赚多少",再更新"历史最低买入价"。

4.3 返回值部分

  • return maxProfit;

    遍历完后,记录的就是全局最大利润;如果一直没赚到,仍是 0。


5. 本题相关知识点补充

5.1 贪心的核心思想(本题为什么天然贪心)

本题的"卖出价"每天都在变化,但"如果今天卖出,最优买入一定是历史最低价"。

所以我们只需要维护一个历史最小值,不需要回头枚举所有组合。

5.2 动态规划视角(帮助你建立模型)

你也可以把它理解成 DP:

  • 定义:dp[i] 表示到第 i 天为止的最大利润

  • 转移:
    dp[i] = max(dp[i-1], prices[i] - minPriceSoFar)

  • 其中 minPriceSoFarmin(prices[0..i])

最终 dp[n-1] 就是答案。

实现时我们把 dp 压缩掉,只保留 maxProfitminPrice,就是上面的写法。

5.3 易错点

  1. 把"最低价"更新顺序写反:推荐"先用今天价格计算利润,再更新 minPrice",逻辑更清晰

  2. 误以为可以买卖多次:本题只允许一次交易(多次的是 122)

  3. 忘了不能亏钱:因此答案至少是 0,而不是负数


6. 复杂度分析

  • 时间复杂度: O(n)(只遍历一遍数组)

  • 空间复杂度: O(1)(只用常数变量)


7. 总结

本题一句话总结:

遍历价格数组,维护"历史最低买入价 minPrice",每一天都尝试作为卖出日更新最大利润。

你学到的技巧:

  • 用"历史最小值"把 O(n^2) 枚举优化成 O(n)

  • 贪心 / DP(状态压缩)在这种"前缀最优"问题里非常常见

类似题推荐(股票系列刷题路线):

  • 122:买卖股票的最佳时机 II(可多次交易)

  • 123:买卖股票的最佳时机 III(最多两次交易)

  • 309:最佳买卖股票时机含冷冻期

  • 714:买卖股票的最佳时机含手续费


8. 扩展写法(可选)------暴力法(理解用,提交会超时)

java 复制代码
class Solution {
    public int maxProfit(int[] prices) {
        int maxProfit = 0;
        for (int i = 0; i < prices.length - 1; i++) {
            for (int j = i + 1; j < prices.length; j++) {
                maxProfit = Math.max(maxProfit, prices[j] - prices[i]);
            }
        }
        return maxProfit;
    }
}

为什么超时:两层循环会做约 n(n-1)/2 次比较,n=10^5 时数量级太大。

相关推荐
wuweijianlove20 分钟前
算法复杂度估算的实验建模与可视化表达的技术6
算法
执笔画流年呀20 分钟前
7大排序算法
java·算法·排序算法
AI成长日志26 分钟前
【算法学习专栏】动态规划基础·中等两题精讲(198.打家劫舍、322.零钱兑换)
学习·算法·动态规划
计算机安禾29 分钟前
【数据结构与算法】第28篇:平衡二叉树(AVL树)
开发语言·数据结构·数据库·线性代数·算法·矩阵·visual studio
测试_AI_一辰29 分钟前
AI 如何参与 Playwright 自动化维护:一次自动修复闭环实践
人工智能·算法·ai·自动化·ai编程
未来之窗软件服务42 分钟前
算法设计—计算机等级考试—软件设计师考前备忘录—东方仙盟
算法·软件设计师·计算机等级考试
未来之窗软件服务1 小时前
哈夫曼树构造—计算机等级考试—软件设计师考前备忘录—东方仙盟
算法·软件设计师·计算机等级考试·仙盟创梦ide·东方仙盟
SUNNY_SHUN1 小时前
VLM走进农田:AgriChat覆盖3000+作物品类,607K农业视觉问答基准开源
论文阅读·人工智能·算法·开源
黎阳之光1 小时前
视频孪生赋能车路云一体化,领跑智慧高速新征程
人工智能·算法·安全·数字孪生
Darkwanderor2 小时前
高精度计算——基础模板整理
c++·算法·高精度计算