"给血雨腥风的二级市场留下八个大字——巴菲特就那么回事"
题目链接:122. 买卖股票的最佳时机2 。给定一个数组 prices,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一只 股票。(买卖多次)返回你能获得的最大利润 。
输入:prices = [7,1,5,3,6,4]$
输出:
解释:在第 天(股票价格 = )的时候买入,在第 天(股票价格 = )的时候卖出, 这笔交易所能获得利润 = 。 随后,在第 天(股票价格 = )的时候买入,在第 天(股票价格 = )的时候卖出, 这笔交易所能获得利润 = 。 总利润为 。
输入:
输出:
解释:在第 天(股票价格 = )的时候买入,在第 天 (股票价格 = )的时候卖出, 这笔交易所能获得利润 = 。 总利润为 。
与 121. 买卖股票的最佳时机 相比,去掉了 交易次数 的限制,因此我们去掉 数组中 交易次数 这一维度。
方法1: 中规中矩的动态规划
1、确定 dp 状态数组以及含义
定义 ,在 天数区间内,持股状态为 时的最大利润
其中,
-
为天数,,
-
为是否持股, 或 , 为未持股状态, 为持股状态
2、确定 dp 状态转移方程
第 天,未持股,即,最大利润就有两种可能:
-
第 天,持股,第 天卖出(交易变为1次),即
-
第 天,未持股,即
我们取两者的最大值,即
第 天,持股,即 ,最大利润就有两种可能:
-
第 天,未持股,但第 天买入,即
-
第 天,持股,但第 天什么也不干,即
我们取两者的最大值,即
3、确定 dp 初始状态
-
,代表第 天不持股,手上最大的利润;
-
,代表第 天持股,手上最大的利润;
4、确定遍历顺序
从第 天遍历到第 天
5、确定返回值
第 天的最大利润,一定是手上不持股时的利润(即 ),即返回
6、上代码
/**
* 空间复杂度:平均O(n),n 是 prices 数组的长度
* 时间复杂度:平均O(n)
*/
function maxProfit(prices: number[]): number {
const n = prices.length;
if (n < 2) {
return 0;
}
const dp = new Array(n).fill(null).map(() => [0, 0]);
dp[0][0] = 0;
dp[0][1] = -prices[0];
for(let i = 1; i < n; 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[n - 1][0];
}
方法2: dp数组状态压缩
从法1的状态转移方程可以看出来, 仅与 与 有关系,故可将 数组压缩成两个值 和 。
示例代码
/**
* 空间复杂度:平均O(1)
* 时间复杂度:平均O(n)
*/
function maxProfit(prices: number[]): number {
const n = prices.length;
if (n < 2) {
return 0;
}
let profit = 0, costed = - prices[0];
for(let i = 1; i < n; i ++) {
[profit, costed] = [
Math.max(profit, costed + prices[i]),
Math.max(costed, profit - prices[i]),
];
}
return profit;
}
🔴 NOTE: 上述示例代码中的 解构赋值 语法很重要。
[profit, costed] = [
Math.max(profit, costed + prices[i]),
Math.max(costed, profit - prices[i]),
];
等价于
const tempProfit = profit;
profit = Math.max(profit, costed + prices[i]);
costed = Math.max(costed, tempProfit - prices[i]);
拓展方法--贪心
由于股票的购买没有限制,因此整个问题等价于寻找 个不相交区间 使得,,其中 表示第 天买入, 表示第 卖出。上式可整理为:
从贪心的角度考虑,我们每次选择贡献值大于 的区间即为最大收益,即
示例代码
/**
* 空间复杂度:平均O(1)
* 时间复杂度:平均O(n)
*/
function maxProfit(prices: number[]): number {
let profit = 0;
for(let i = 1, len = prices.length; i < len; i ++) {
profit += Math.max(0, prices[i] - prices[i - 1]);
}
return profit;
}