「前端刷题」122.买卖股票的最佳时机 II(MEDIUM)

254 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

题目(Best Time to Buy and Sell Stock II)

链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii
解决数:3312
通过率:69.9%
标签:贪心 数组 动态规划 
相关公司:amazon bytedance facebook 

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

思路

解法一:暴力枚举

  • 时间复杂度:O(n^n) 指数级
  • 空间复杂度:O(n) 递归深度为n
  • 递归枚举所有可能情况,取其中最大值
  • 设可获最大价值max
  • 设当天买卖股票可或临时最大价值maxProfit
  • 设当天start
  • 结果
    • 超出时间限制
/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    return calc(prices,prices.length,0)
};
function calc(prices,len,start){
    if(start >= len){
        return 0;
    }
    var max = 0;
    for(var startIndex = start;startIndex prices[startIndex]){
                // 当前剩余价值+当前价值-第一天起始点价值 == 当前组合的总价值
                var profit = calc(prices,len,i+1) + prices[i] - prices[startIndex];
                // 更新当天与第i天 最大价值和
                if(profit > maxProfit){
                    maxProfit = profit;
                }
            }
        }
        // 更新每天价值最大值的和
        if(maxProfit > max){
            max = maxProfit;
        }
    }
    return max;
}

解法二:全局贪婪匹配 == 阶段1贪婪匹配 + 阶段2贪婪匹配 + ... + 阶段n贪婪匹配 如有只要钻石会员不要黄金会员!!!

  • 原理类似正则表达式贪婪匹配模式
    • 趋向于最大长度匹配
    • 例如
      • str = "abcaxc"
      • patter p = "ab.*c"
      • 匹配结果为 "abcaxc"
        • 对应本题的在最低点买入,最高点卖出,利润在某个阶段内才会最大
        • 借用官方一张图说明
          • 如图所示
            • 假设第一天从valley(i)开始,交易最后一天是在peak(j)结束,求最大利润
              • 遍历
                • 第一个阶段的最低点恰好是起点valley(i),最高点为peak(i)
                  • 当前阶段最大利润是 peak(i) - valley(i) == A
                • 第二个阶段的最低的是vallery(j),最高点为peak(j)
                  • 当前阶段最大利润是 peak(j) - valley(j) == B
            • 所以 A+b 即为所求全阶段最大利润
    • 应用到本题即是第一个起点开始 循环
      1. 若下一个节点是下降 即 第i+1天到价格 小于 第i天的价格
      • 则将起点记录为临时 最高价格节点,i++ ,
        • 循环
          • 直到第k天小于第k+1天时,说明开始上升
          • 则将第k天记录为从 i到k阶段 的 最低价格节点
      • 则此阶段最大利润等于 最高-最低节点价格 == prices[k] - prices[i]
      1. 若下一个节点是上升 即 第i+1天到价格 大于 第i天的价格
      • 则将起点记录为临时 最低价格节点,i++ ,
        • 循环
          • 直到第k天大于第k+1天时,说明开始下降
          • 则将第k天记录为从 i到k阶段 的 最高价格节点
      • 则此阶段最大利润等于 最高-最低节点价格 == prices[k] - prices[i]
    • n轮外循环结束
      • 所有交易日的最大利润 == 各个阶段的最大利润和
/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    // 谷值
    var valley = prices[0];
    // 峰值
    var peak = prices[0];
    // 最大利润值
    var maxProfit = 0;
    var lenNeed = prices.length - 1;
    var i = 0;
    while(i < lenNeed){
        while(i < lenNeed && prices[i] > prices[i+1]){
            i++;
        }
        valley = prices[i];
        while(i < lenNeed && prices[i] <= prices[i+1]){
            i++;
        }
        peak = prices[i];
        maxProfit += peak - valley;
    }
    return maxProfit;
};

解法三:贪心算法 = 相邻两天利润大于0则抢走 一个不留不管下一个 既要芝麻也要西瓜!!!

  • 原理类似正则表达式非贪婪匹配模式
    • 趋向于最短长度匹配
      • 明天比今天价格高,今天买入明天就卖出
      • 以后的每一天都是如此重复不留到第三天
    • 例如
      • str = "abcaxc"
      • patter p = "ab.*?c"
      • 匹配结果为 "abc"
        • 对应本题就是只要第二天比第一天价格上涨就卖出
        • 借用官方一张图说明
        • D = A + B +C
          • 而不再需要等遍历到D时再算 peak(D) - valley(A)
/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    // 最大利润值
    var maxProfit = 0;
    var len = prices.length;
    var i = 0;
    for(var i = 1;i prices[i-1]){
            // 只要明天赚了就抛售获得利润存钱包里
            maxProfit += prices[i] - prices[i-1];
        }
    }
    return maxProfit;
};

解法四:动态规划

/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    let n = prices.length;
    if(n == 0){
        return 0;
    }
    let dp = Array.from(new Array(n),() => new Array(2));
    for(let i = 0;i < n;i++){
        if(i == 0){
            dp[0][0] = 0;
            dp[0][1] = -prices[0];
            continue;
        }
        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]);
    }
    return dp[n-1][0];
};

解法五:动态规划 + 降维

/**
 * @param {number[]} prices
 * @return {number}
 */
var maxProfit = function(prices) {
    let n = prices.length;
    if(n == 0){
        return 0;
    }
    let dp_i_0 = 0;
    let dp_i_1 = -Infinity;
     for(let i = 0;i < n;i++){
        var tmp = dp_i_0; 
        dp_i_0 = Math.max(dp_i_0,dp_i_1+prices[i]);
        dp_i_1 = Math.max(tmp-prices[i],dp_i_1);
    }
    return dp_i_0;
};