leetcode -[动态规划、最大连续子数组] - 买卖股票的最佳时机(121)

本文通过动态规划解析了六道股票买卖问题,包括只能进行一次交易、不限交易次数、最多两次交易等场景,提供了通用的方法框架。通过列举所有可能的「状态」,用状态转移方程进行穷举更新,最终找到最大收益。动态规划的核心在于理解状态和状态转移,通过不断迭代找到最优解。
摘要由CSDN通过智能技术生成

1、问题描述

给定一个数组,数组中的第i个元素表示一支给定股票第i天的价格。
如果你最多只许完成一笔交易,即只能买入一次和卖出一次,
设计一个算法来计算你能获得的最大收益。
注意,你不能在买入股票前卖出股票。

示例1:

输入:[7,1,2,5,3,6]
输出:5
解释:在第2天的时候买入(股票价格为1),第6天的时候卖出(股票价格为6),可以获得最大收益6-1 = 5.

示例2:

输入:[7,6,4,3,1]
输出:0 。
解释:当天买入,当天卖出不会出现亏损。

2、解题思路

解决这个问题有以下几种方法:

  • 方法1:暴力法。使用指针i、j双层循环,外部循环i表示在第i天的时候买入,内部循环j表示在第j天的时候就卖出,根据要求,j必须大于i,因此有i的遍历范围为1到数组长度,j的遍历范围为i+1到数组长度。很明显,这种方法的时间复杂度为 O ( n 2 ) O(n^2) O(n2),空间复杂度为 O ( 1 ) O(1) O(1)
  • 方法2:动态规划。这个方法有两个关键变量,maxprofit和minprice。
    maxpofit:表示到第j天之前获得的最大收益;
    minprice:记录第j天之前,股票的最低价格;
(1) j从头开始遍历价格数组prices:
	(2)如果在第j天卖出时获得的收益大于maxprofit,则更新			maxprofit,即maxprofit = max(maxprofit,prices[j] - minprice);
	(3)如果第j天股票的价格prices[j]小于minprice,则更新minprices,即minprice =min(minprice,prices[j])

这种方法的时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1).

  • 方法3:转换成求最大连续子数组的问题。
    求出价格数组相邻两天价格的差值,得到一个新的差值数组。
    然后在差值数组上求最大连续子数组的和即为可获得的最大收益。
    这种方法的基本思想是,假设你在重复这样的操作,不断地在第i天买入,第i+1天卖出,求累积的收益。
    这种方法的时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n).

3、代码实现

方法2:

class Solution {
   
public:
    int maxProfit(vector<int>& prices) {
   
        if(prices.size() == 0 || prices.size() == 1){
   
            return 0;
        }
        int maxprofit = 0;
        int minprice = prices[0];
        for(int j = 0; j < prices.size(); j++){
   
            maxprofit = max(maxprofit, prices[j] - minprice);
            minprice = min(minprice, prices[j]);
        }
        return maxprofit;
    }
};

方法2:

/*方法1:
思想:转换成求最大连续子数组的问题,在原数组上求相邻两个元素的差值得到一个新的数组,然后在新的数组上求最大连续子数组。
*/
class Solution {
   
public:
    int maxProfit(vector<int>& prices) {
   
        if(prices.size() == 0 || prices.size() == 1){
   
            return 0;
        }
        vector<int> gain;
        for(int i = 0; i < prices.size() - 1; i++){
   
            gain.push_back(prices[i+1] - prices[i]);
        }
        int ngreatest = gain[0];
        int cursum = gain[0];
        for(int j = 1; j < gain.size(); j++){
   
            if(cursum < 0){
   
                cursum = gain[j];
                
            }
            else{
   
                cursum += gain[j];
            }
            
            if(cursum > ngreatest){
   
                ngreatest = cursum;
                
            }
        }
        
        ngreatest = max(ngreatest, 0);
        
        return ngreatest;


    }
};

4、拓展问题

拓展1: 如果允许多次交易,即多次买入和卖出一支股票,求能获得的最大利润。

问题链接:122. 买卖股票的最佳时机 II
分析:这一拓展问题的难点在于,要获得最大利润,交易的次数是未知的。解决这道问题有以下几种方法:
方法1:暴力搜索。使用这种方法时,我们需要计算所有可能的交易组合相对应的最大利润。交易组合的产生是通过在每一天,根据当天是否持有股票来选择相应的操作(操作的种类包括不操作、买入、卖出)、交易组合所对应的解空间树如下图所示:
在这里插入图片描述
代码实现

/*算法思想:
深度优先遍历交易组合的解空间树
*/
class Solution {
   
    int maxprofit;
public:

    int maxProfit(vector<int>& prices) {
   
        if(prices.size() < 2){
   
            return 0;
        }
        int status = 0;
        int curprofit = 0;
        // int maxprofit = 0;
        int day = 0;
        maxprofit = 0;

        dfs(prices, day,  status, curprofit);
        return maxprofit;

    }
    
    /*day:表示搜索的第几天
      status: 表示当前是否持有股票;
      curprofit:搜索到当前节点的利益和;
      maxprofit: 最大利益*/
    void dfs(vector<int>& prices, int day, int status, int curprofit){
   
        // cout<<"day="<<day<<endl;
        if(day == prices.size()){
   
            maxprofit = max(maxprofit, curprofit);
            return ;
        }
        /*保持当前状态,没有股票仍然不买入,有股票不卖出*/
        dfs(prices, day + 1, status, curprofit);

        /*如果当天没有股票,尝试买入股票,否则尝试卖出股票*/
        if(status == 0){
   
            dfs(prices, day + 1, 1, curprofit - prices[day]);
        }
        else{
   
            dfs(prices, day + 1, 0, curprofit + prices[day]);
        }

    }
};

方法2:动态规划。
(1) 定义状态:
根据交易组合的解空间树,我们可以使用dp[i][j]来表示到第i天为止,持有股票状态为j时的所获得的最大收益。
i的取值范围为[0,prices.length - 1];
j的取值范围为{0, 1},1表示有股票,0表示没有股票。
(2) 确定状态转移方程

  • 状态从持有现金开始,到最后一天我们关心的状态仍然是持有现金
  • 每一天的状态可以保持不变,也可以转移,状态转移用下图表示:
    在这里插入图片描述
  • 因为交易次数不受限制,因此除了最后一天,每一天都可能的状态都可能保持不变,也可能转移。
  • 经过以上分析:状态转移方程如下:
    d p [ i ] [ j ] = { m a x { d p [ i − 1 ] [ 0 ] 、 d p [ i − 1 ] [ 1 ] + p r i c e s [ i ] } j = 0 ; m a x { d p [ i − 1 ] [ 1 ] 、 d p [ i − 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Albert_YuHan

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

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

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

打赏作者

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

抵扣说明:

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

余额充值