leetcode121:买卖股票的最佳时机Ⅰ(贪心思想入门)


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

二、 核心思路:贪心策略 - 维护最低买入价

要获得最大利润,我们肯定希望在价格最低的时候买入,在价格最高的时候卖出。但关键在于,买入必须发生在卖出之前。对于只允许进行一次交易的情况,我们不需要考虑复杂的波峰波谷匹配,而是可以采用一种非常直接的贪心策略:

  1. 遍历每一天的价格:对于每一天 prices[i],我们都假设它可能是我们卖出的日子
  2. 寻找最佳买入价:如果 prices[i] 是卖出日,那么为了最大化利润,我们必须在 i 之前的日子里,找到一个最低的买入价格
  3. 更新最大利润:计算 prices[i] 减去这个最低买入价格的差值,并与之前记录的最大利润进行比较,取较大者
  4. 实时更新最低买入价:在遍历过程中,我们始终维护一个变量 prevMin,记录到目前为止遇到的最低股票价格。这样,当处理 prices[i] 时,prevMin 就代表了 i 之前(包括 i 自己)的最低买入价

这个策略之所以是贪心的,是因为在每一步,我们都只关心当前能够获得的最大利润,并实时更新历史最低价格,而不需要回溯或考虑未来的趋势


三、 贪心算法的扩展介绍

贪心算法(Greedy algorithm)通常用于优化问题,其中需要在一组可能的选择中选择最佳解决方案,以最大化或最小化某个目标函数。贪心算法基于当前情况下的最佳选择,而不关心全局的最优解。在每一步,贪心算法都选择当前情况下的最佳选择,并且不会回回头更改之前的选择

特色

  1. 局部最佳选择:贪心算法选择的是 局部最优 选择,即在每一步都尽量达到最有利的结果,而不考虑全局最优解
  2. 无回头修改:一旦贪心算法做出了选择,就不会回头更改。这意味着它不考虑之前的决策对未来决策的影响
  3. 适用性:贪心算法适用于具有最佳子结构性质和无后效性的问题
  4. 效率:贪心算法通常具有较低的计算成本和更快的执行速度
  5. 不保证最优解:贪心算法不保证找到全局最优解,因为它忽略了全局的考虑

总之,贪心算法是一种简单而有效的解决问题方法,特别适用于某些问题,其中局部最佳选择能够导致全局最优解。然而,它不适用于所有问题,且无法保证最优解,因此在应用时需要谨慎考虑问题特性和要求

在本题中,贪心策略是:在每一天,我们都尝试用当前遇到的最低价格买入,然后用当天的价格卖出,计算这个潜在利润。
由于我们只允许一次交易,且买入必须在卖出之前,这个局部最优选择(即“用当前最低价买入,用当前价卖出”)的累计最大值,最终就是全局最优解


四、 最佳实践:代码实现与深度解析

一种贪心思想的代码实现:

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 是一个入门级贪心算法问题,在某些情况下,通过局部最优的选择(即在每个可能的卖出点,都找到其之前的最低买入点),最终能够达到全局最优解

进阶:

下一题,基于“股票买卖问题”的进阶题目,同样基于贪心思想,只不过有些细微差别,难度上相差并不大,趁热打铁巩固练习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值