leetcode - [121]买卖股票的最佳时机|刷题打卡

176 阅读1分钟

1. 题目描述

[121] 买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
    
示例 1:
 输入:[7,1,5,3,6,4]
 输出:5
 解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。

注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
    
示例 2:
 输入:prices = [7,6,4,3,1]
 输出:0
 解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
    
    
提示:
1 <= prices.length <= 105
0 <= prices[i] <= 104
Related Topics 数组 动态规划

2. 思路分析

​ 理解题目,主要的想法还是在遍历的过程中,找到一个最小值,也就是最便宜的价格,然后再后面的天数卖出去。

​ 我的想法是再边遍历的时候边计算,如果值小于当前记录的最便宜的值,那说明更适合买入。如果不适合买入,那试试卖出的价格是不是比之前最大的卖出值更大,如果是,说明这就是一个更优的买卖方式。(method1、method3)

​ method2比较符合动态规划的做法,详细看注释

3. AC代码

public class No121 {
    public int maxProfit(int[] prices) {
        return method1(prices);
//        return method2(prices);
//        return method3(prices);
    }

    /**
     * 理解同method1
     *
     * https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/mai-mai-gu-piao-de-zui-jia-shi-ji-bao-li-yywe/
     * 执行耗时:3 ms,击败了47.07% 的Java用户
     * 内存消耗:51.2 MB,击败了50.44% 的Java用户
     * @param prices
     * @return
     */
    private int method3(int[] prices) {
        int n = prices.length;
        if (n < 2) return 0;

        int min = Integer.MAX_VALUE;
        int result = 0;
        // 时间比method1 多,应该因为每次循环都需要判断两次
        for (int i=0;i<n;i++) {
            //从左边开始找到最低价格
            min = Math.min(min, prices[i]);
            //第i天的价格与在这之前的最低价进行做差,最终得到最大利润
            result = Math.max(result, prices[i] - min);
        }
        return result;
    }

    /**
     * 更像是用了动态规划,不过感觉效果没有method1直观
     *
     * https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/121-mai-mai-gu-piao-de-zui-jia-shi-ji-zu-1v0i/
     * 执行耗时:4 ms,击败了27.47% 的Java用户
     * 内存消耗:51.4 MB,击败了38.89% 的Java用户
     * @param prices
     * @return
     */
    private int method2(int[] prices) {
        int dp0 = 0;                    // 一直不买
        int dp1 = - prices[0];          // 只买了一次
        int dp2 = Integer.MIN_VALUE;    // 买了一次,卖了一次

        for(int i = 1; i < prices.length; i++){
            // 价格更优惠,买
            dp1 = Math.max(dp1, dp0 - prices[i]);
            // dp1 是负值,加上当前值的数,比较大说明卖出价格划算
            dp2 = Math.max(dp2, dp1 + prices[i]);
        }
        // dp2还有可能是 负值,负值不如不买,选择dp0
        return Math.max(dp0, dp2);
    }

    /**
     * 初始值为0,因为有可能不交易
     *
     * 内存消耗:51.1 MB,击败了54.15% 的Java用户
     * 内存消耗:38.4 MB,击败了61.17% 的Java用户
     * @param prices
     * @return
     */
    private int method1(int[] prices) {
        // 初始化值
        int maxEarnPrice = 0;
        int cheapestPrice = prices[0];

        for (int i = 0; i < prices.length; i++) {
            // 如果有更小的值,按说明更适合购买
            if (cheapestPrice > prices[i]) {
                cheapestPrice = prices[i];
            } else {
                int earnPrice = prices[i] - cheapestPrice;
                // 如果当前遍历到的值比已记录的最大收益额更大,那说明更适合卖出
                maxEarnPrice = Math.max(earnPrice,maxEarnPrice);
            }
        }
        return maxEarnPrice;
    }
}

4. 总结

​ 结合leetcode - [53]最大子序和 ,感觉有那么一丝丝理解动态规划,不知道大家有没有感觉(可能也是题目简单)。不过,最重要还是多多联系,冲冲冲


​ 本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情