算法题 买卖股票的最佳时机

题目:

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。

注意:你不能在买入股票前卖出股票。

 

示例 1:

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

示例 2:

输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

分析:

首先,卖出必须在买入之后,而当数组中“稳亏不赚”的情况下,可以选择不买也不卖,也就是利润为0。否则应该尽量提高利润,也就是选择更小的买入价格和更大的卖出价格,卖出价格一定在买入价格之后

在这道题中有两种情况比较特殊:

  1. 给定数组为[]。
  2. 给定数组长度为1。

情况1:收益必定为0,没什么好考虑。

情况2:一旦买入则无法卖出,稳亏不赚,所以只能选择不买也不卖,所以也直接返回0即可。

当给定数组长度大于等于2的时候,就需要遍历计算了。现在以示例中的数组为例:

[7,1,5,3,6,4],设置一个整型变量sum来记录最大收益,sum初始值为0(因为最坏的情况就是利润为0,只要利润大于0,就必须将sum覆盖掉)。

设置一个整型变量curr来记录当前阶段的最小买入价格。

假设给定数组为示例数组的前两位[7,1],那么假设利润不为0,那么一定是在第一天买入,第二天卖出,所以curr只能为数组[0]的值,经计算1-7可知,与利润为0相比,亏到吐血,所以sum的值不改变。

然后假设给定数组为示例数组的前三位[7,1,5],那么假设利润不为0,可能是在第二天卖出,也可能是在第三天卖出,由于第二天卖出的利润我们已经计算过了,并且将其与无利润的情况做了对比,将最优解保存在了sum中,所以,sum的值为当前阶段(数组为[7,1,5]时),之前所有阶段(数组为[7,1]时)的最优解。首先,我们需要重新计算curr的值,原因就是在上一阶段curr只有可能是7,而在当前阶段,curr可能是7或1,curr中保存的值可以视为在当前阶段(数组为[7,1,5]时),之前所有阶段(数组为[7,1]时)的最小买入价格,而当前阶段新增的可买入价格只有1,所以只需要将1与curr相比较,取出最小值,即可得出当前阶段的最小买入价格。

以此类推,我们可以发现,假设在第n天卖出股票的利润最大,则最大利润 = 第n天的股票价格 - 最小买入价格[第1天 ~ 第n - 1天]。

代码:

 

class Solution {
    public int maxProfit(int[] prices) {
        if (prices.length == 0 | prices.length == 1) {
            return 0;
        }
        int sum = 0;
        int curr = prices[0];
        for (int i = 0, j = 1; j < prices.length; j++) {
            curr = Math.min(prices[j - 1], curr);
            sum = Math.max(prices[j] - curr, sum);
        }
        return sum;
    }
}

 

为了练习动态规划找的一道题,不知道我这个思路算不算是动态规划,如果光看文字和代码比较凌乱,可以尝试自己手动画一下图来理解,一图胜千言!之所以要记录一下最小买入价格其实是为了避免掉一些不必要的计算,也算是一个小技巧吧(建议可以去了解一下滚动数组和记忆化递归)。

 

posted @ 2020-07-11 14:44  无心大魔王  阅读(701)  评论(0编辑  收藏  举报