int maxProfit(int* prices, int pricesSize) {
int hold = -prices[0]; //持有股票最大利润
int noHold = 0; //不持有股票最大利润
for(int i=1;i<pricesSize;i++)
{
int newHold = noHold - prices[i];
int newNoHold = hold + prices[i];
hold = hold > newHold ? hold : newHold;
noHold = noHold > newNoHold ? noHold : newNoHold;
}
return noHold;
}
语言:C
分析日期:2026-10-18
用户代码来源:用户在本次对话中直接粘贴,属于 user pasted 证据。
1. 题目要求
给定数组 prices,其中:
prices[i] = 第 i 天的股票价格
每天可以选择买入、卖出或不操作,但任何时刻最多持有一股股票。允许进行任意多次交易,目标是最大化总利润。
题目约束:
1 <= prices.length <= 3 * 10^4
0 <= prices[i] <= 10^4
示例:
| 输入 | 输出 | 说明 |
|---|---|---|
[7,1,5,3,6,4] |
7 |
1 -> 5 获利 4,3 -> 6 获利 3 |
[1,2,3,4,5] |
4 |
可以从 1 持有到 5,也可以逐日交易 |
[7,6,4,3,1] |
0 |
没有正利润,选择不交易 |
2. 最关键的识别:这是"无限次交易",不是"两次交易"
题目没有规定交易次数上限。只要前一笔交易已经卖出,就可以继续寻找下一次买入机会。
例如:
prices = [1, 5, 2, 4, 1, 6]
最佳交易为:
1 -> 5,利润 4
2 -> 4,利润 2
1 -> 6,利润 5
总利润 = 11
因此,算法不能只寻找一个买点和一个卖点,也不能只把数组切成两段后分别求最大利润。
3. 你的两个版本在做什么
你的代码可以拆成两个部分:
3.1 maxPF:求一段区间内的一次交易最大利润
你的函数:
int maxPF(int *prices, int pricesSize) {
int D = *(prices), Zhi = 0;
for (int i = 0; i < pricesSize; i++) {
if (D > *(prices + i)) {
D = *(prices + i);
} else {
Zhi = *(prices + i) - D > Zhi
? *(prices + i) - D
: Zhi;
}
}
return Zhi;
}
这里:
-
D表示扫描到当前位置之前的最低价格; -
Zhi表示到当前位置为止的一次交易最大利润; -
每天要么更新最低买入价,要么尝试用当前价格卖出。
在 pricesSize >= 1 的题目约束下,这个函数的核心逻辑是正确的,时间复杂度为 O(n),额外空间复杂度为 O(1)。
它解决的是:
只允许完成一次买入和一次卖出时的最大利润
并不是 LeetCode 122 的完整问题。
3.2 maxProfit:尝试把数组切成两段
你的 maxProfit 先统计相邻价格的下降、上升和相等次数,然后处理完全单调的特殊情况;对于普通情况,再枚举分割位置:
前半段:用 Zhi1 记录一笔交易的利润
后半段:调用 maxPF 求一笔交易的利润
All = 两段利润之和的最大值
抽象成公式就是:
maxProfit(prices)
≈ max over split k:
oneTrade(prices[0..k]) + oneTrade(prices[k+1..n-1])
这实际上是在求:
最多两次交易的最大利润
而题目需要的是:
任意多次交易的最大利润
这不是小的实现偏差,而是问题模型不匹配。
4. 初版代码的问题
4.1 prices + i < pricesSize - 2 是错误的比较
初版中出现:
if (prices + i < pricesSize - 2)
左侧 prices + i 是指针,右侧 pricesSize - 2 是整数。这里本来应该判断下标:
if (i < pricesSize - 2)
指针和整数不能作为这里的逻辑条件进行比较。该表达式不是你想要的"是否还有足够元素",并且在标准 C 中不具备可移植的正确含义。
修改版将它改为:
if (i < pricesSize - 2)
这项修改是必要的。
4.2 Zhi1 保存成了价格,而不是利润
两个版本中都有这一句:
Zhi1 = *(prices + i) - D1 > Zhi1
? *(prices + i)
: Zhi1;
条件判断使用的是:
当前价格 - 最低价格 > 当前最大利润
但条件成立后,三目运算符保存的却是:
当前价格
应该保存:
当前价格 - 最低价格
最小反例:
prices = [1, 5, 2, 4]
正确的无限次交易利润是:
1 -> 5:4
2 -> 4:2
总利润:6
你的代码在扫描到 5 时令:
Zhi1 = 5
然后后缀 [2,4] 的 Best = 2,于是可能得到:
All = 5 + 2 = 7
这已经大于真实可获得的利润,说明结果不只是"没有达到最优",而是出现了不可能的虚假利润。
局部修正应为:
Zhi1 = *(prices + i) - D1 > Zhi1
? *(prices + i) - D1
: Zhi1;
但是,修正这个赋值仍然不能解决"最多两次交易"和"O(n^2)"这两个根本问题。
4.3 只枚举两个区间,无法覆盖三次及以上有利交易
反例:
prices = [1, 5, 2, 4, 1, 6]
最佳利润是:
(5 - 1) + (4 - 2) + (6 - 1)
= 4 + 2 + 5
= 11
你的拆分模型最多只能把这些上涨区间分配到两个部分,因此不可能表达三笔交易。
这正是本题最重要的反例:即使把 Zhi1 的赋值修好,算法仍然不符合题意。
4.4 特殊计数分支不是必需的
你的代码统计:
int d = 0, z = 0, g = 0;
并根据下降、相等、上升的数量提前返回:
if (z == pricesSize - 1) return 0;
if (z + d == pricesSize - 1) return 0;
if (g + z == pricesSize - 1)
return prices[pricesSize - 1] - prices[0];
这些分支在部分单调情况下的结果是合理的,但它们没有改变算法的核心复杂度,也没有覆盖一般情况。推荐算法只需一次线性扫描,完全不需要先统计三种相邻关系。
5. 修改版比初版改善了什么
修改版将:
if (prices + i < pricesSize - 2)
改成了:
if (i < pricesSize - 2)
这是明确的语法语义修复,避免了指针与整数的错误比较。
但修改版仍保留:
Zhi1 = *(prices + i) - D1 > Zhi1
? *(prices + i)
: Zhi1;
因此仍然存在"把价格当成利润"的严重正确性错误。
即使进一步修正为:
Zhi1 = *(prices + i) - D1 > Zhi1
? *(prices + i) - D1
: Zhi1;
它最多也只能成为"最多两次交易"的 O(n^2) 解法,不是本题的推荐解。
6. 参考贪心解法
别人给出的代码是:
int maxProfit(int *prices, int pricesSize) {
int profit = 0;
for (int i = 1; i < pricesSize; i++) {
if (prices[i] > prices[i - 1]) {
profit += prices[i] - prices[i - 1];
}
}
return profit;
}
它的核心规则只有一句:
只累加所有相邻的正增长
例如:
[7, 1, 5, 3, 6, 4]
相邻差值:
-6, +4, -2, +3, -2
只累加正数:
4 + 3 = 7
7. 为什么逐日累加正差值是正确的
7.1 一段连续上涨可以拆成每天交易
对于连续上涨区间:
1 -> 2 -> 3 -> 4 -> 5
从 1 买入、5 卖出的利润是:
5 - 1 = 4
逐日交易的利润是:
(2 - 1) + (3 - 2) + (4 - 3) + (5 - 4)
= 4
两者相等。
所以,连续上涨时,持有到波峰与每天在上涨时段完成一次买卖,利润完全相同。
7.2 下降日不应该交易
如果:
prices[i] <= prices[i - 1]
从前一天持有到今天不会增加利润,因此当天差值贡献为 0。
对于相等价格:
prices[i] - prices[i - 1] = 0
加不加都不影响答案。
7.3 所有上涨段的利润可以相加
把数组划分为若干个连续上涨段。每个上涨段的总利润等于该段中所有正相邻差值之和;下降段不产生正利润。
因此全局最优值就是:
sum(max(prices[i] - prices[i - 1], 0))
8. Mermaid 控制流图
flowchart TD
A[开始] --> B[profit = 0]
B --> C{i 从 1 扫描到 pricesSize - 1}
C -->|prices[i] > prices[i-1]| D[profit += prices[i] - prices[i-1]]
C -->|prices[i] <= prices[i-1]| E[跳过当天差值]
D --> C
E --> C
C -->|扫描结束| F[返回 profit]
9. 正确性证明
设:
delta[i] = prices[i] - prices[i - 1]
算法返回:
P = Σ max(delta[i], 0)
对任意一段从第 l 天到第 r 天的连续上涨区间,有:
prices[r] - prices[l]
= (prices[l+1] - prices[l])
+ (prices[l+2] - prices[l+1])
+ ...
+ (prices[r] - prices[r-1])
因此,把一次长交易拆成该上涨区间内的逐日交易,不会改变利润。
所有下降或相等的相邻差值都不会增加利润,跳过它们不会损失最优解。由于题目允许无限次交易,且每天最多持有一股,所有互不重叠的上涨段可以分别完成交易并累加。
所以:
算法返回的正差值总和 = 所有上涨段的最大可获得利润
算法正确。
10. 复杂度比较
| 方法 | 时间复杂度 | 额外空间 | 是否符合题意 | 主要问题 |
|---|---|---|---|---|
你的 maxPF |
O(n) |
O(1) |
只适合一次交易 | 不能处理多次交易 |
你的 maxProfit |
最坏 O(n^2) |
O(1) 额外空间 |
否 | 最多两段、最多两次交易;且原代码有赋值错误 |
| 修改赋值后的两段法 | O(n^2) |
O(1) |
仍不完全符合 | 只能表达最多两次交易 |
| 参考贪心法 | O(n) |
O(1) |
是 | 无明显问题 |
maxProfit 的 O(n^2) 来自:
每个分割点都调用一次 maxPF
若后缀长度分别为 n-1、n-2、...、1,总工作量约为:
(n - 1) + (n - 2) + ... + 1 = O(n^2)
而题目要求的数组长度最多为 3 * 10^4,线性算法更合适。
11. 推荐 C 实现
int maxProfit(int *prices, int pricesSize)
{
int profit = 0;
for (int i = 1; i < pricesSize; ++i) {
if (prices[i] > prices[i - 1]) {
profit += prices[i] - prices[i - 1];
}
}
return profit;
}
如果只按题目给出的价格范围计算,最大总利润不会超过:
(pricesSize - 1) * 10000
在 pricesSize <= 30000 时约为 2.9999 * 10^8,可以放入 32 位有符号 int。如果把函数复用到更大的生产数据范围,建议改用 long long 并重新检查返回类型。
12. 测试矩阵
| 测试输入 | 期望 | 覆盖点 |
|---|---|---|
[7,1,5,3,6,4] |
7 |
题目示例,多段上涨 |
[1,2,3,4,5] |
4 |
全程上涨 |
[7,6,4,3,1] |
0 |
全程下降 |
[1] |
0 |
单元素 |
[1,1,1,1] |
0 |
全部相等 |
[1,5,2,4] |
6 |
两次交易,检测"价格误当利润" |
[1,5,2,4,1,6] |
11 |
三次交易,检测"两段模型" |
[2,3,2,4,1,5,1,6] |
12 |
多个上涨段 |
[5,4,3,2,1,2,3] |
2 |
下降后重新上涨 |
[0,10000,0,10000] |
20000 |
价格边界 |
30000 个价格在 0 和 10000 间交替 |
149995000 |
大规模线性扫描 |
针对你代码的缺陷,至少应保留以下回归用例:
[1,5,2,4] -> 6
[1,5,2,4,1,6] -> 11
第一组可以暴露 Zhi1 保存价格而非利润的问题;第二组可以暴露"最多两次交易"的模型限制。
13. 结论
你的 maxPF 是一个正确的一次交易子程序,但 maxProfit 把本题误解成了"把数组分成两段、每段最多交易一次"。
具体结论:
-
初版的
prices + i < pricesSize - 2应改为i < pricesSize - 2。 -
Zhi1的三目表达式应该保存利润差值,而不是当前价格。 -
即使修正上述问题,两段递归/分割思路仍然只能处理最多两次交易。
-
本题允许无限次交易,最简单且正确的抽象是累加所有相邻的正差值。
-
推荐使用参考贪心实现,时间复杂度
O(n),额外空间复杂度O(1)。
来源
-
LeetCode 122 官方题目描述:题意、交易规则、示例和约束。访问日期:2026-10-18。
-
用户在本次对话中粘贴的初版、修改版和参考 C 代码:用于个人实现评审。