LeetCode 121,买卖股票的最佳时机,【难度:简单;通过率:59.1%】,这道题是“股票系列”中最基础的一道,它要求我们只进行 一次 买卖操作,以获取最大利润。如何高效地找到这个最佳时机呢——巧妙运用 贪心算法
一、 题目描述
给定一个数组 prices,其中 prices[i] 是第 i 天的股票价格
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出这只股票
设计一个算法来计算你所能获取的最大利润。如果你不能获取任何利润,返回 0
示例:
示例 1:
输入:prices = [7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5
注意:不能在第 1 天(股票价格 = 7)买入,然后第 1 天卖出
示例 2:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易能获得利润,所以返回 0
二、 核心思路:贪心策略 - 维护最低买入价
要获得最大利润,我们肯定希望在价格最低的时候买入,在价格最高的时候卖出。但关键在于,买入必须发生在卖出之前。对于只允许进行一次交易的情况,我们不需要考虑复杂的波峰波谷匹配,而是可以采用一种非常直接的贪心策略:
- 遍历每一天的价格:对于每一天
prices[i],我们都假设它可能是我们卖出的日子 - 寻找最佳买入价:如果
prices[i]是卖出日,那么为了最大化利润,我们必须在i之前的日子里,找到一个最低的买入价格 - 更新最大利润:计算
prices[i]减去这个最低买入价格的差值,并与之前记录的最大利润进行比较,取较大者 - 实时更新最低买入价:在遍历过程中,我们始终维护一个变量
prevMin,记录到目前为止遇到的最低股票价格。这样,当处理prices[i]时,prevMin就代表了i之前(包括i自己)的最低买入价
这个策略之所以是贪心的,是因为在每一步,我们都只关心当前能够获得的最大利润,并实时更新历史最低价格,而不需要回溯或考虑未来的趋势
三、 贪心算法的扩展介绍
贪心算法(Greedy algorithm)通常用于优化问题,其中需要在一组可能的选择中选择最佳解决方案,以最大化或最小化某个目标函数。贪心算法基于当前情况下的最佳选择,而不关心全局的最优解。在每一步,贪心算法都选择当前情况下的最佳选择,并且不会回回头更改之前的选择
特色:
- 局部最佳选择:贪心算法选择的是 局部最优 选择,即在每一步都尽量达到最有利的结果,而不考虑全局最优解
- 无回头修改:一旦贪心算法做出了选择,就不会回头更改。这意味着它不考虑之前的决策对未来决策的影响
- 适用性:贪心算法适用于具有最佳子结构性质和无后效性的问题
- 效率:贪心算法通常具有较低的计算成本和更快的执行速度
- 不保证最优解:贪心算法不保证找到全局最优解,因为它忽略了全局的考虑
总之,贪心算法是一种简单而有效的解决问题方法,特别适用于某些问题,其中局部最佳选择能够导致全局最优解。然而,它不适用于所有问题,且无法保证最优解,因此在应用时需要谨慎考虑问题特性和要求
在本题中,贪心策略是:在每一天,我们都尝试用当前遇到的最低价格买入,然后用当天的价格卖出,计算这个潜在利润。
由于我们只允许一次交易,且买入必须在卖出之前,这个局部最优选择(即“用当前最低价买入,用当前价卖出”)的累计最大值,最终就是全局最优解
四、 最佳实践:代码实现与深度解析
一种贪心思想的代码实现:
class Solution {
public int maxProfit(int[] prices) {
// ans 用于记录到目前为止的最大利润,初始为 0
int ans = 0;
// prevMin 用于记录到目前为止的最低股票价格,初始为第一天的价格
int prevMin = prices[0];
// 从第二天开始遍历,因为第一天无法卖出
for (int i = 1; i < prices.length; i++) {
// 计算如果今天卖出,能获得的利润
int todayProfit = prices[i] - prevMin;
// 如果今天能获利 (todayProfit > 0),则更新最大利润
// 注意:这里是取最大值,而非累加,因为只能进行一次交易
if (todayProfit > 0) {
ans = Math.max(ans, todayProfit);
}
// 无论今天是否获利,我们都要更新 prevMin
// prevMin 始终保持为到当前天 prices[i] 为止的最低价格
prevMin = Math.min(prevMin, prices[i]);
}
return ans;
}
}
提交结果:

五、 关键点与复杂度分析
- 贪心策略:核心在于维护一个
prevMin变量,它代表了当前日期之前(包括当前日期)可以买入的最低价格 - 一次遍历:算法只需一次遍历数组,非常高效
- 时间复杂度:O(N) 其中 N 是
prices数组的长度 - 空间复杂度:O(1) 只使用了几个常数级别的额外变量
- 与滑动窗口的关联:虽然我们没有显式地定义左右指针,但这个过程可以看作是一个特殊的滑动窗口。其中
prevMin相当于窗口的左边界(买入点),prices[i]相当于窗口的右边界(卖出点)。窗口的左边界会根据遇到的更低价格而向右“收缩”或“移动”
六、 小结与进阶
LeetCode 121 是一个入门级贪心算法问题,在某些情况下,通过局部最优的选择(即在每个可能的卖出点,都找到其之前的最低买入点),最终能够达到全局最优解
进阶:
下一题,基于“股票买卖问题”的进阶题目,同样基于贪心思想,只不过有些细微差别,难度上相差并不大,趁热打铁巩固练习
&spm=1001.2101.3001.5002&articleId=150719828&d=1&t=3&u=7368345917894d17b7a894926dc1e289)
1954

被折叠的 条评论
为什么被折叠?



