一起养成写作习惯!这是我参与「掘金日新计划 · 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 * 1040 <= 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
- 第一个阶段的最低点恰好是起点valley(i),最高点为peak(i)
- 遍历
- 所以 A+b 即为所求全阶段最大利润
- 假设第一天从valley(i)开始,交易最后一天是在peak(j)结束,求最大利润
- 如图所示
- 应用到本题即是第一个起点开始 循环
- 若下一个节点是下降 即 第i+1天到价格 小于 第i天的价格
- 则将起点记录为临时 最高价格节点,i++ ,
- 循环
- 直到第k天小于第k+1天时,说明开始上升
- 则将第k天记录为从 i到k阶段 的 最低价格节点
- 循环
- 则此阶段最大利润等于 最高-最低节点价格 == prices[k] - prices[i]
- 若下一个节点是上升 即 第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;
};