【温故知新】`122. 买卖股票的最佳时机 II` 贪心算法、动态规划实现

359 阅读3分钟

往期股票问题

【温故知新】121. 买卖股票的最佳时机 动态规划、双指针实现

题目描述

给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

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

输入: prices = [1,2,3,4,5] 输出: 4 解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。 示例 3:

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

提示:

1 <= prices.length <= 3 * 104 0 <= prices[i] <= 104

解题思路

贪心算法

  • 单独交易日:假设当天加个为p1,第二天价格为p2,则第二天卖出的利润为profit=p2-p1(profit<0,则亏损不卖)
  • 连续上涨交易日:假设连续上涨交易价格为p1,p2,p3,...pn;则第一天买入,第n天卖出;最大利润profit=pn-p1;
    • 这种情况等价于每天都买入卖出;pn-p1=(p2-p1)+(p3-p2)+...(pn-pn-1);
  • 连续亏损交易日:不买卖;

时空复杂度

  • 时间复杂度 O(N) : 只需遍历一次price;
  • 空间复杂度 O(1) : 变量使用常数额外空间。

代码

function maxProfit(prices: number[]): number {
    
    let profit:number=0;
    for(let i=1;i<prices.length;i++){
         let res=prices[i]-prices[i-1];
        if(res>0){ // 如果有利润就卖出;可以当天卖出并在当天再买入的情况下;
            profit+=res;
        }// 
    }// end of for loop
    return profit
};

动态规划

动态规划三部曲
  • 定义dp含义
    • 定义dp[n][2]:
    • n代表第n天有2种状态,持有或者不持有
    • dp[i][0] 第i天不持有获得的最大利润;
    • dp[i][1] 第i天持有获得的最大利润;(tip:是持有不是买入)
  • 定义状态转移方程:
    • dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i])

    对于今天不持有,可以从两个状态转移过来:1. 昨天也不持有;2. 昨天持有,今天卖出。两者取较大值。

    • dp[i][1]=Math.max(dp[i-1][0]-prices[i],dp[i-1][1]);

    对于今天持有,可以从两个状态转移过来:1. 昨天也持有;2. 昨天不持有,今天买入。两者取较大值。

  • 初始化dp;
    • 不持有:dp[0][0]=0;
    • 持有:dp[0][1]=-prices[0] ((即花了price[0] 的钱买入)) 由于dp[n][0]>dp[i][1],当天不持有股票利润一定大于当天持有股票,因此返回dp[len-1][0]即可

时空复杂度

  • 时间复杂度:O(n),遍历一遍即可。
  • 空间复杂度:O(n)

代码

function maxProfit(prices: number[]): number {
  // 动态规划;
  // 定义dp[n][2]:
     // dp[i][0] 第i天不持有获得的最大利润;
     // dp[i][1] 第i天持有获得的最大利润;
  // 定义状态转移方程:
      
    // dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i])
    // dp[i][1]=Math.max(dp[i-1][0]-prices[i],dp[i-1][1]);

   // dp初始化
    // dp[0][0]=0;
    // dp[0][1]=-prices[0]
    let len=prices.length;
    let dp=new Array(len);
    for(let i=0;i<len;i++){
        dp[i]=new Array(2).fill(0)
    }
    dp[0][0]=0;
    dp[0][1]=-prices[0]

    for(let i=1;i<len;i++){
      dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i])
      dp[i][1]=Math.max(dp[i-1][0]-prices[i],dp[i-1][1]);
    }// end of for loop

    return dp[len-1][0] 

};

优化:状态转移的时候,dp[i] 只会从 dp[i-1] 转移得来,因此第一维可以去掉:

时空复杂度

  • 时间复杂度:O(n),遍历一遍即可。
  • 空间复杂度:O(1)
function maxProfit(prices: number[]): number {
  // 动态规划;
    let len=prices.length;
    let dp=new Array(2).fill(0);

    dp[0]=0;
    dp[1]=-prices[0]

    for(let i=1;i<len;i++){
        let temp=dp[0]
      dp[0]=Math.max(dp[0],dp[1]+prices[i])
      dp[1]=Math.max(temp-prices[i],dp[1]);
    }// end of for loop

    return dp[0] 

};

往期动态规划回顾

完全背包🎒问题

【温故知新】322. 零钱兑换 动画演示-完全背包问题最小解-动态规划实现

【温故知新】518. 零钱兑换 II 完全背包问题组合解-动态规划实现

【温故知新】377. 组合总和 Ⅳ 完全背包问题排列解-动态规划实现

【温故知新】70. 爬楼梯进阶版 完全背包🎒问题AC-动态规划实现

01背包🎒问题

【温故知新】474. 一和零 01背包🎒问题最大解-动态规划实现

【温故知新】494. 目标和 表达式转化为01背包🎒问题-动态规划实现

【温故知新】1049. 最后一块石头的重量 II 最小重量转化为01背包🎒问题最大解-动态规划实现

【温故知新】416. 分割等和子集01背包🎒问题存在解-动态规划实现