LeetCode 75 —— 121. 买卖股票的最佳时机

51 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情

LeetCode 75 —— 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

来源:力扣(LeetCode) 链接:leetcode.cn/problems/be… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

二、思路分析:

  1. 这道题考察了什么思想?你的思路是什么?

    这道题我看到的第一想法是暴力解法:

    对数组中的每一个元素,比较其后面元素与之的利润差,取利润最大值即可。

    但是这种方法并不妥当,经过深思熟虑,我想到了一个时间复杂度为O(n)的方法:

    设置一个变量,存储遍历数组时,遇到的最低点,同时使用一个变量存储最大利润,每次将当前值与最低点进行求利润,然后与最大利润比较,这样貌似是可行的,我们来试一下吧!

  1. 做题的时候是不是一次通过的,遇到了什么问题,需要注意什么细节?

    不是一次通过的,下面这种解法存在问题:

    func maxProfit(prices []int) int {
        var low int = math.MaxInt64
        var maxProfit int = 0
    ​
        for num := range prices {
            if low > num {
                low = num
            }
            fmt.Println(low,num)
            if maxProfit < (num - low) {
                maxProfit = num - low
            } 
            fmt.Println(maxProfit)
        }
    ​
        return maxProfit
    }
    

    当测试数据为[7,6,4,3,1]时,它会输出4,与预期结果0不符:

    0 0
    0
    0 1
    1
    0 2
    2
    0 3
    3
    0 4
    4
    

    这是什么问题呢?

    经过排查,我发现num := range prices时获取的全是数组索引,所以我们将该语句改为:_,num := range prices即可。

  2. 有几种解法,哪种解法时间复杂度最低,哪种解法空间复杂度最低,最优解法是什么?其他人的题解是什么,谁的效率更好一些?用不同语言实现的话,哪个语言速度最快?

    image-20221005091706162

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int ans = 0;
        vector<int> St;
        prices.emplace_back(-1); \ 哨兵👨•✈️
        for (int i = 0; i < prices.size(); ++ i){
            while (!St.empty() && St.back() > prices[i]){ \ 维护单调栈📈
                ans = std::max(ans, St.back() - St.front()); \ 维护最大值
                St.pop_back();
            }
            St.emplace_back(prices[i]);
        }
​
        return ans;
    }
};
​
作者:wen-mu-yang
链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/solution/c-li-yong-shao-bing-wei-hu-yi-ge-dan-diao-zhan-tu-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

三、AC 代码:

func maxProfit(prices []int) int {
    var low int = math.MaxInt64
    var maxProfit int = 0
    for _,num := range prices {
        if low > num {
            low = num
        }
        fmt.Println(low,num)
        if maxProfit < (num - low) {
            maxProfit = num - low
        } 
        fmt.Println(maxProfit)
    }
    return maxProfit
}

四、总结:

暴力法的时间复杂度为O(n^2),空间复杂度为O(1),一次遍历法的时间复杂度为O(n),而其空间复杂度也为O(1)。

这说明有时候如果光掌握暴力解法是不行的,在数据量很大的情况下,可能会导致代码运行时间很长。

文慕阳大佬提出的使用利用哨兵👨‍✈️,维护一个单调栈,用 O(n) 的时间得知所有位置两边第一个比他大(或小)的数的位置。值得一学!

模板来源:

作者:掘金酱

链接:juejin.cn/post/706970…

来源:稀土掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。