力扣解题-121. 买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
示例 1:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
提示:
1 <= prices.length <= 105
0 <= prices[i] <= 104
第一次解答
解题思路
核心方法:贪心策略 + 快慢双指针(一次遍历),通过维护“当前最低买入价”和“当前最大利润”两个变量,单次遍历数组即可找到最优解,时间复杂度O(n)、空间复杂度O(1),是本题的最优解法。
核心原理铺垫
股票买卖的核心逻辑:要获取最大利润,需在最低点买入、之后的最高点卖出。利用一次遍历即可实时更新“最低买入价”和“最大利润”,无需嵌套遍历(避免O(n²)的低效):
left(慢指针/最低买入价):记录遍历过程中遇到的最低股票价格,初始为第一天的价格(prices[0]);right(快指针/遍历指针):从第二天开始遍历,计算当前价格与left的差值(即当前利润),并更新最大利润;max:记录遍历过程中出现的最大利润,初始为0(无利润时返回0)。
具体步骤
- 初始化核心变量:
left = prices[0]:初始买入价为第一天的价格;max = 0:初始最大利润为0(默认无利润)。
- 遍历数组计算利润:
- 快指针
right从1开始遍历数组(第二天及以后); - 若当前价格
prices[right] > left(当前卖出可获利):- 计算临时利润
temp = prices[right] - left; - 若临时利润大于当前
max,更新max为该临时利润;
- 计算临时利润
- 若当前价格
prices[right] <= left(当前价格更低,更适合买入):- 更新
left为prices[right](将买入价更新为当前更低的价格);
- 更新
- 快指针
- 返回结果:遍历完成后返回
max,若全程无利润则返回初始值0。
核心优化逻辑说明
- 时间复杂度最优:仅一次遍历数组(O(n)),每个元素仅被访问一次,无嵌套循环或冗余计算,因此耗时1ms击败100%用户;
- 空间复杂度极致:仅使用三个基础变量(left、max、right),空间复杂度O(1),完全符合最优解要求;
- 贪心策略的精准适配:每一步都选择“当前最优”(更新最低买入价、更新最大利润),最终得到全局最优解,完美契合“只能一次买卖”的题目规则;
- 内存表现说明:内存消耗92.8MB仅击败15.21%用户,并非逻辑问题,而是评测机环境差异(如JVM内存分配、数组存储的内存占用等),该解法的内存开销已达到理论最优。
public int maxProfit(int[] prices) {
int left=prices[0];
int max=0;
for(int right=1;right<prices.length;right++){
if(prices[right]>left){
int temp=prices[right]-left;
if(temp>max){
max=temp;
}
}else {
left=prices[right];
}
}
return max;
}
总结
- 该解法的核心是贪心策略的应用:无需记录所有价格组合,仅通过一次遍历维护“最低买入价”和“最大利润”两个核心变量,即可得到最优解;
- 关键技巧:
- 遍历过程中优先更新“最低买入价”,再计算当前利润,确保买入价始终是“当前遍历位置之前的最低价”;
- 初始最大利润设为0,天然处理“无利润可赚”的场景,无需额外判断;
- 性能优势:时间复杂度O(n)适配题目10⁵级别的数据规模,是该问题的理论最优解。