Leetcode算法之买卖股票的最佳时机

135 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

LeetCode 121 买卖股票的最佳时机

题目描述:

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

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

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

题目分析:

根据最后一句描述,返回的结果应该为两个。若存在利润,则返回最大利润;若不能获得利润,也就是这几天里面都是亏本的,则返回0。则有公式:

f(i,j)={max(profit(i,j)),profit(i,j)>00,profit(i,j)<=0f(i, j) = \begin{cases} max(profit(i, j))&, profit(i, j) > 0 \\0&,profit(i, j)<=0 \end{cases}

其中profit(i, j)表示第i天到第j天的收益,即:

profit(i,j)=prices[j]prices[i]profit(i, j) = prices[j] - prices[i]

要求出那一段时间利润最大,那就是找局部最优梯度的过程。局部最优梯度需保证两个条件:

1.上升初始点为局部最小值点(极小值点)

2.局部函数终止点为初始点右侧局部最高点(极大值点)

因此,求最大收益的过程也就转变成了寻找极小值点和极小值右侧部分的最高点问题。根据梯度更新规则:

if prices[j] - min(prices[i]) > gradient: gradient:=prices[j] - min(prices[i])\begin{aligned} if \text{ prices[j] - min(prices[i]) > gradient:} \\ &\text{ gradient:=prices[j] - min(prices[i])}\end{aligned}

示例1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

根据输入绘制这1~6天的股票价格折线图,进行分析

a0dcf49a05aad8cae3f77907ec8bfb60.png

1~6天股票折线图 | 图片来源:CodeItEasy

选取第二天作为股票买入时间点,恰好买入点为极小值点,分别在第三天、第四天、第五天、第六天卖出股票,计算收益。之后,再往后查找比第二天这个极小值更小的点,计算不同时间点卖出的收益,发现第二天买入是最低的价格点。

e8dde532a450c1b30a9bdc980cd306d0.png

寻找最优上升梯度 | 图片来源:CodeItEasy

通过图片,可以很直观得看到,蓝色虚线的上升梯度最大,也就是选择在第五天卖出是收益最大的。

4f5008db70488021c0b77b41485f5ffb.png

两个极小点寻找最优梯度的过程 | 图片来源:CodeItEasy

如上图所示,股票价格出现了第二天和第五天的两个极小值点,且可以看到绿色虚线为第二天买入点的最佳卖出策略,黑色虚线为第五天买入点的最佳卖出策略。比较两个买入点的梯度,绿色虚线的策略更优,具有更高的收益。因此,选择在第二天买入,第四天卖出是最好的选择。

根据我们的选择策略,结合公式分析,不难可以推导出以下算法:

class Solution {
    public int maxProfit(int[] prices) {
        int min = prices[0];
        int max = 0;
        for(int i = 1; i < prices.length; i++) {
            if(prices[i] < min) {
                min = prices[i];
            } else if(prices[i] - min > max) {
                max = prices[i] - min;
            }
        }
        return max;
    }
}