买卖股票的最佳时机 II
问题描述
给定一个数组 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。 - 总利润 = 4 + 3 = 7。
-
示例 2:
输入:prices = [1,2,3,4,5] 输出:4 解释: - 第 1 天买入(价格 = 1),第 5 天卖出(价格 = 5),利润 = 5 - 1 = 4。 - 总利润 = 4。
-
示例 3:
输入:prices = [7,6,4,3,1] 输出:0 解释: - 在这种情况下,没有交易可以获得正利润,所以最大利润为 0。
解题思路
-
贪心算法:由于可以多次交易,我们可以采用贪心算法,只要今天的价格高于昨天的价格,就进行买卖操作,从而累加所有的利润。
-
关键点:
- 持股限制:在任何时候最多只能持有一股股票。
- 当天买卖:可以在同一天买入并卖出股票。
-
实现思路:
- 遍历价格数组,从第 1 天开始(索引为 1)。
- 比较当天价格和前一天价格的差值:
- 如果差值大于 0,说明价格上涨,可以获得利润。
- 将差值累加到总利润中。
- 遍历结束后,返回总利润。
算法步骤
-
初始化变量:
totalProfit = 0
:用于累加总利润。
-
遍历价格数组:
- 从索引
i = 1
开始,直到prices.length - 1
。 - 对于每个
i
,计算profit = prices[i] - prices[i - 1]
。- 如果
profit > 0
,则将profit
加到totalProfit
中。
- 如果
- 从索引
-
返回结果:
- 遍历完成后,返回
totalProfit
,即最大利润。
- 遍历完成后,返回
代码解释
function maxProfit(prices: number[]): number {
let totalProfit = 0; // 初始化总利润
for (let i = 1; i < prices.length; i++) {
const profit = prices[i] - prices[i - 1]; // 计算当天与前一天的价格差
if (profit > 0) {
totalProfit += profit; // 如果有利润,累加到总利润中
}
}
return totalProfit; // 返回总利润
}
-
变量初始化:
totalProfit
:用于累加所有的正利润。
-
遍历数组:
for (let i = 1; i < prices.length; i++) { ... }
- 从第二天开始遍历,因为需要与前一天的价格比较。
-
计算利润并累加:
const profit = prices[i] - prices[i - 1]; if (profit > 0) { totalProfit += profit; }
- 计算当天价格与前一天价格的差值。
- 如果差值大于 0,表示价格上涨,可以获得利润,将其累加到
totalProfit
。
-
返回结果:
return totalProfit;
示例演示
以 prices = [7,1,5,3,6,4]
为例:
-
初始化:
totalProfit = 0
-
遍历过程:
-
i = 1
:prices[1] - prices[0] = 1 - 7 = -6
,利润为负,跳过。
-
i = 2
:prices[2] - prices[1] = 5 - 1 = 4
,利润为正,totalProfit += 4
,totalProfit = 4
。
-
i = 3
:prices[3] - prices[2] = 3 - 5 = -2
,利润为负,跳过。
-
i = 4
:prices[4] - prices[3] = 6 - 3 = 3
,利润为正,totalProfit += 3
,totalProfit = 7
。
-
i = 5
:prices[5] - prices[4] = 4 - 6 = -2
,利润为负,跳过。
-
-
结果:
totalProfit = 7
时间和空间复杂度分析
-
时间复杂度:O(n),其中 n 是价格数组的长度。需要遍历一次数组。
-
空间复杂度:O(1),只使用了常数级别的额外空间。
边界情况处理
-
价格单调递增:
- 例如
prices = [1,2,3,4,5]
,总利润为4
。
- 例如
-
价格单调递减:
- 例如
prices = [7,6,5,4,3]
,无法获得正利润,totalProfit = 0
。
- 例如
-
价格数组长度为 1 或 0:
- 无法进行交易,利润为
0
。
- 无法进行交易,利润为
与问题一的区别
-
在第一题(只能进行一次交易)中,我们需要找到一个最佳的买入和卖出时机,以获得最大利润。
-
在本题中(可以多次交易),我们可以多次买卖股票,只要有利润就进行交易。
扩展
-
限制交易次数:如果限制交易次数为
k
,需要使用动态规划来解决。 -
增加手续费:如果每次交易需要支付手续费,需要在计算利润时减去手续费。
测试代码
function testMaxProfit() {
const testCases = [
{ prices: [7, 1, 5, 3, 6, 4], expected: 7 },
{ prices: [1, 2, 3, 4, 5], expected: 4 },
{ prices: [7, 6, 4, 3, 1], expected: 0 },
{ prices: [1, 2, 1, 2, 1, 2], expected: 3 },
];
for (let { prices, expected } of testCases) {
const result = maxProfit(prices);
console.assert(result === expected, `测试失败:输入 ${prices},期望输出 ${expected},实际输出 ${result}`);
}
console.log("所有测试用例通过!");
}
testMaxProfit();
总结
-
核心思想:只要今天的价格高于昨天的价格,就买入并卖出,累加所有正利润。
-
贪心策略:局部最优解(每次有利润就交易)能够得到全局最优解(最大总利润)。
-
实现简单:代码简洁明了,易于理解和实现。