LeetCode121&122.买卖股票的最佳时期(C++)

前言

今天记录一下 「LeetCode121.买卖股票的最佳时期」和「LeetCode122.买卖股票的最佳时期Ⅱ」 两个问题的解法。先理解如何利用贪心算法解决第二题,再去想第一题,立马就能类比得到解决问题的思路,将其转化为最大连续子列和问题,并利用在线处理算法得到结果。

LeetCode122.买卖股票的最佳时期Ⅱ

给定一个数组 prices ,其中 prices[i]表示一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。
你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例1

|| 输入:[7,1,5,3,6,4]
|| 输出:7
|| 解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

示例2

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

贪心算法解决

如下图所示的一个股票曲线图,可以直观地看到,只需要找到 「股票开始上涨的最小值」和「股票上涨到的最大值」 ,在最小值点买入股票并在最大值点卖出股票,计算这一段之间的差,就是折线递增区间的增量,也就是这段时间内股票的最大利润。最后只需要把所有上涨时间段内的利润累加起来就是要求的结果。
1
贪心算法就是指,在对问题求解的时候,总是做出当前来看是最好的选择,某种意义上就是局部最优解。

在这道题目中,只要是在上升的,我们就计算上升的增量,然后对所有增量进行累加,并不需要找到这段完整增量区间的最小值和最大值再来求差值。

我们结合图片来理解一下,当连续几天股价都在增长的情况下,可以看成在上升的第二个点,我先卖出再买入。连续下降的时候我就不买入,这一天我手中是没有股票的。

2
也可以结合数学公式来理解,在一段上升区间 a<b<c,则这段区间的增量是 (c-a),同样可以这样计算 (b-a)+(c-b),到这一步应该一切都明明白白了。

我们只要把 prices 数组遍历一遍,并且在遍历的过程中用后一项减前一项,如果是正数则累加,负数则不加,得到 4+3=7
3
这个时候代码就呼之欲出了:

class Solution{
    public:
    int maxProfit(vector<int>&prices){
        int total = 0;
        for(int i=0;i<prices.size()-1;i++){
            total += max(prices[i+1]-prices[i],0);
        }
        return total;
    }
};

LeetCode121.买卖股票的最佳时期

给定一个数组 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

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

转化为最大连续子列和问题

这道题是让求完成一笔交易所获得的最大利润,并且在有交易完成的情况下,只能买入一次和卖出一次,有了上一题的经验,很容易将这个问题转化为「最大连续子列和」 的问题。

如下图展示的股票曲线图,上一题是避免了所有股票下跌的情况,只要上升我的利润就增加。而这道题我只能在某个区间的起点买入,在终点卖出,这个区间中股价有涨有跌,但总体上来说我是获利的,否则我一开始就不会买入。
4
通过观察可以发现,在这个区间的最小值点买入,在最大值点卖出,中间即便股价下跌也不会跌出最小值,即便有涨也不会超出最大值。

那么我们要怎么找出使得利润最大的这个区间呢?回顾上一题,我们用数组的后一个值减前一个值,并且将差为正的数累加,得到利润。而这一题,得到所有的差值之后,要使某个子区间内的差都累加起来(无论正负)的值达到最大,那么求 [7,1,5,3,6,4] 中最大利润的问题就转化为了求 [-6,4,-2,3,-2] 中最大连续子列和的问题。
5

在线处理算法解决

如果我们使用for循环把所有子列和情况都算出来再求出最大值的话,那显然时间复杂度太大了。所以这里采用「在线处理算法」,即使数据一个一个读入,返回的最大连续子列和也是当前正确的,算法时间复杂度为O(n)

对于待求数组 [-6,4,-2,3,-2] ,我们将每一项向右累加,发现更大的和则更新当前结果,如果当前子列和为负,它一定会使后面的数字变得更小,干脆抛弃,将当前子列和置为0,最后得到的结果即为最大连续子列和。

只要一次遍历,将数组 prices 的后一项减去前一项,得到的值进入「在线处理算法」,相当于数据一个一个读入,得到最大连续子列和,即最大利润。思路有了,代码同样是呼之欲出:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int len = prices.size();
        int maxSum = 0;   //最大利润
        int thisSum = 0;  //当前子列和
        for(int i=1;i<len;++i){
            thisSum += prices[i]-prices[i-1];
            if(thisSum>maxSum)
                maxSum = thisSum;  //发现更大的和则更新当前结果
            if(thisSum<0)
                thisSum = 0; //如果当前子列和为负.它一定会使后面的数变得更小,干脆抛弃,置和为0
        }
        return maxSum;
    }
};

微信公众号文章

LeetCode121&122.买卖股票的最佳时期

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dululuya

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值