代码随想录day42|121买卖股票的最佳时机122买卖股票的最佳时机II(动规)|01笔记

54 阅读4分钟
  • 121买卖股票的最佳时机

  • 代码随想录 (programmercarl.com)
  • 第一印象

  • 第一印象是使用贪心,因为这支股票只交易一次,所以只要找到先出现的最小值以及求得后面可以卖出的最大值就可以了。
  • 讲解观后感

  • 依旧是类似前几次动态规划的思考步骤。
  • dp[i][0] 表示第i天持有股票所得最多现金,dp[i][1] 表示第i天不持有股票所得最多现金。注意这里说的是“持有”,“持有”不代表就是当天“买入”!也有可能是昨天就买入了,今天保持持有的状态
  • 如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来
    • 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]
    • 第i天买入股票,所得现金就是买入今天的股票后所得现金即:-prices[i]
  • 那么dp[i][0]应该选所得现金最大的,所以dp[i][0] = max(dp[i - 1][0], -prices[i]);
  • 如果第i天不持有股票即dp[i][1], 也可以由两个状态推出来
    • 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1]
    • 第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:prices[i] + dp[i - 1][0]
  • 同样dp[i][1]取最大的,dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
  • 初始化:dp[0][1]表示第0天不持有股票,不持有股票那么现金就是0
    • 所以第一天持有就是dp[0][0] -= prices[0];不持有就是dp[0][1] = 0;
  • 解题代码

  • 贪心
  •       func maxProfit(prices []int) int {
              maxPro := 0
              minB := math.MaxInt64
          
              for i:=0;i<len(prices);i++ {
                  minB = min(minB, prices[i])
                  maxPro = max(maxPro, prices[i] - minB)
              }
              return maxPro
          }
          
          func min(x int,y int) int {
              if x > y {
                  return y
              } else {
                  return x
              }
          }
          func max(x int,y int) int {
              if x > y {
                  return x
              } else {
                  return y
              }
          }
    
  • 动态规划1
  •       func maxProfit(prices []int) int {
          	length := len(prices)
          	if length == 0{return 0}
          	dp := make([][]int,length)
          	for i := 0; i < length; i++ {
          		dp[i] = make([]int, 2)
          	}
          	dp[0][0] = -prices[0]
          	dp[0][1] = 0
          	for i := 1; i < length; i++ {
          		dp[i][0] = max(dp[i-1][0], -prices[i])
          		dp[i][1] = max(dp[i-1][1], dp[i-1][0] + prices[i])
          	}
          	return dp[length-1][1]
          }
          
          func max(a, b int) int {
              if a > b {
                  return a 
              }
              return b 
          }
    
  • 时间复杂度:O(n);空间复杂度:O(n)
  • 动态规划2:
  • 因为我们的dp推导只需要上一个的值,所以我们只需要维护一个空间为2的dp就可以。这样能够减少空间复杂度。
  •       func maxProfit(prices []int) int {
              dp := [2][2]int{} // 注意这里只开辟了一个2 * 2大小的二维数组
              dp[0][0] = -prices[0]   
              dp[0][1] = 0
              for i := 1; i < len(prices); i++ {
                  dp[i%2][0] = max(dp[(i-1)%2][0], -prices[i])
                  dp[i%2][1] = max(dp[(i-1)%2][1], dp[(i-1)%2][0]+prices[i])
              }
          
              return dp[(len(prices)-1)%2][1]
          }
          
          func max(a, b int) int {
              if a > b{
                  return a 
              }
          
              return b
          }
    
  • 时间复杂度:O(n);空间复杂度:O(1)
  • 122买卖股票的最佳时机II

  • 代码随想录 (programmercarl.com)
  • 第一印象

  • 还是只有一支股票,并且只能持有一股。这种确定的状态决定了可以使用状态转移公式。也可以使用贪心。之前在学习贪心的时候,用的是确保每次价格上涨的时候都记录利润即可。因为每一天都可以同时卖出买入,所以利润是可以叠加传递的。
  • 讲解观后感

  • 使用动态规划的方法时,与股票I唯一的区别的就是要注意递推公式的变化。因为我们的持有现金可能存在之前已经买卖后的利润。
  • dp数组的含义:
    • dp[i][0] 表示第i天持有股票所得现金。dp[i][1] 表示第i天不持有股票所得最多现金。
  • 初始dp[0][0], dp[0][1] = 0, -prices[0]
  • 转移公式:
  • 之前的情况,只能买卖一次,所以当前持有的情况dp[i][0]只需要比较上一次持有dp[i-1][0]-price[i]。而这一次需要考虑之前买卖过的情况,所以-price[i]要变为dp[i - 1][1] - prices[i]
  •           dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); // 注意这里是和121. 买卖股票的最佳时机唯一不同的地方。
              dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i]);
    
  • 解题代码

  • 动规1
  •       func maxProfit(prices []int) int {
              dp := make([][]int, len(prices))
              for i := 0; i < len(dp); i++ {
                  dp[i] = make([]int, 2)
              }
              // dp[i][0]表示在状态i不持有股票的现金,dp[i][1]为持有股票的现金
              dp[0][0], dp[0][1] = 0, -prices[0]
              for i := 1; i < len(prices); i++ {
                  dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
                  dp[i][1] = max(dp[i-1][0] - prices[i], dp[i-1][1])
              }
              return dp[len(prices)-1][0]
              
          }
          func max(a, b int) int {
              if a > b {
                  return a
              }
              return b
          }
    
  • 时间复杂度:O(n);空间复杂度:O(n)
  • 动规2,我们依旧可以使用两个位置来维护数组
  •       func maxProfit(prices []int) int {
              dp := [2][2]int{}
              
              // dp[i][0]表示在状态i不持有股票的现金,dp[i][1]为持有股票的现金
              dp[0][0], dp[0][1] = 0, -prices[0]
              for i := 1; i < len(prices); i++ {
                  dp[i%2][0] = max(dp[(i-1)%2][0], dp[(i-1)%2][1] + prices[i])
                  dp[i%2][1] = max(dp[(i-1)%2][0] - prices[i], dp[(i-1)%2][1])
              }
              return dp[(len(prices)-1)%2][0]
              
          }
          func max(a, b int) int {
              if a > b {
                  return a
              }
              return b
          }
    
  • 时间复杂度:O(n);空间复杂度:O(1)
  • 贪心
  •       func maxProfit(prices []int) int {
              var sum int
              for i := 1; i < len(prices); i++ {
                  // 累加每次大于0的交易
                  if prices[i] - prices[i-1] > 0 {
                      sum += prices[i] - prices[i-1]
                  }
              }
              return sum
          }