122. 买卖股票的最佳时机 II|刷题打卡

407 阅读3分钟

一、题目描述:

二、思路分析:

首先在拿到题,读完没有任何思路。先跟昨天的题对比一下,昨天的股票问题在一个数组内求一个区间的最大值,而本题是求在一个数组内不限定区间个数(区间不能交叉)的和的最大值。

首先能够明确一点的是,因为是求最大利润,加上数组并不是有序的,需要对每一种情况都要列举出来,就是穷举?

怎么穷举?循环,递归,这两种都不太适合。

思路一:利用“状态”进行穷举,具体到每一天,看看总共有几种可能的“状态”,在找出每个“状态”对应的“选择”,根据对应的“选择”更新“状态”。

具体到每一天交易完,有两个状态:有股票,没股票,通过 0 和 1 表示,然后通过 i 表示第几天;

继续思考,第 i 天的两种情况(持有股票和不持有股票),拆分来看:

dp[i][0]:表示第 i 天交易完手上没有股票的最大利润,它的情况基于上一天的情况,第 i - 1 交易完就没有股票dp[i-1][0],第 i - 1 天交易完有股票,今天卖掉,获得 prices[i] 的收益;

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

dp[i][1]:表示第 i 天交易完手上有股票的最大利润,第 i - 1 天交易完就没有股票dp[i-1][0],今天需要购入股票,花费 prices[i] 的钱,为dp[i-1][0] - prices[i];第 i - 1天交易完有股票,今天不交易的利润为 dp[i-1][1]

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

然后通过循环计算出每一天的两个状态的最大利润。

考虑边缘情况,第一天的时候,没有前一天的依赖。可以特殊书写。

最后一点,在全部交易结束后,最后一天把股票卖出去的收益一定大于最后一天有股票不卖的钱。所以最后一天的值为 dp[n-1][0]

// Your runtime beats 96.73 % of javascript submissions
// Your memory usage beats 15.79 % of javascript submissions (39.9 MB)
// leetcode 第二次提交
// Your runtime beats 63.6 % of javascript submissions
// Your memory usage beats 10.87 % of javascript submissions (40.2 MB)
var maxProfit = function (prices) {
  // length个元素,每个元素两个状态;
  const dp = new Array(prices.length).fill(new Array(2).fill(0));
  dp[0][0] = 0;
  dp[0][1] = 0 - prices[0];
  for (let i = 1; i < prices.length; i++) {
    dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
    dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
  }

  return dp[prices.length - 1][0];
};

时间复杂度:O(n),一次循环,每次循环中复杂度 O(1)

空间复杂度:O(n),开辟出O(2n)个空间存储规划中的所有状态,O(n) = O(2n)

优化,其实我们求最终的利润的时候,本次状态只跟上次状态相关,那么我们是不是可以通过变量变换优化掉空间复杂度,只存储上一次的两个状态值,分别使用 dp0 代表上一次的 dp[i-1][0],使用 dp1 代表上一次的 dp[i-1][1]


// Your runtime beats 80.32 % of javascript submissions
// Your memory usage beats 49.69 % of javascript submissions (39.5 MB)
var maxProfit = function (prices) {
  dp0 = 0;
  dp1 = 0 - prices[0];
  for (let i = 1; i < prices.length; i++) {
    dp0 = Math.max(dp0, dp1 + prices[i]);
    dp1 = Math.max(dp1, dp0 - prices[i]);
  }

  return dp0;
};

优化完:

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

思路二:从评论学习到的,不限制交易次数,只要收集到所有的上坡合集,一定是利益最大化的。今天比昨天涨了,卖出;今天比昨天跌了,不卖,拆成了一节一节的利润,合并到一起。

// Your runtime beats 80.32 % of javascript submissions
// Your memory usage beats 89.42 % of javascript submissions (39.2 MB)
var maxProfit = function (arr) {
  if (arr == null || arr.length <= 1) return 0;

  let profit = 0;
  for (let i = 1; i < arr.length; i++) {
    if (arr[i] > arr[i - 1]) {  // 卖出有利可图
      profit += (arr[i] - arr[i - 1]);
    }
  }

  return profit;
}

三、总结:

本题对于自己来说是一道新题,代码不难,关键是能想到这个思路比较难,在刚看这道题都蒙了,不知道怎么去找多个区间的和最大,换一种思考方式,通过穷举状态来思考,找当天状态的最大值,需要列举当天的状态的情况,当天状态值的跟之前的相关,之前再跟之前的相关,可以穷举出所有情况,通过比较获得最大值。本题还有一种贪心算法,因为之前也没有接触过,等本次刷完题回顾的时候对每道题在进行一个扩展优化。加油!!!