在前面的博客中,我们探讨了只出现一次的数字 、多数元素 、下一个排列 和寻找重复数 的解法。今天,我们继续分享买卖股票的最佳时机 、跳跃游戏 II 和划分字母区间的详细解析,涵盖解题思路、算法特性及代码实现。
六、121. 买卖股票的最佳时机
题目大意 :给定数组 prices,其中 prices[i]是第 i天的股票价格。只能选择某一天买入 ,并在未来某一天卖出 (卖出日 > 买入日),求最大利润。若无法获利,返回 0。
解题思路:一次遍历找最小买入价
核心思想是动态记录"历史最低买入价",并计算当前价格与最低买入价的利润,更新最大利润:
-
初始化
min_price为无穷大(表示初始时没有买入价),max_profit为0(初始利润为0)。 -
遍历每一天的价格:
-
若当前价格 <
min_price,更新min_price为当前价格(找到更优的买入点)。 -
否则,计算当前价格与
min_price的差值(利润),若大于max_profit,则更新max_profit。
-
代码实现(C++)
class Solution {
public:
int maxProfit(vector<int>& prices) {
int min_price = INT_MAX; // 初始最小价格为无穷大
int max_profit = 0; // 初始最大利润为0
for (int price : prices) {
if (price < min_price) {
min_price = price; // 更新最小买入价
} else {
// 计算当前利润,更新最大利润
max_profit = max(max_profit, price - min_price);
}
}
return max_profit;
}
};
算法特性
-
时间复杂度:O(n),仅需遍历数组一次。
-
空间复杂度:O(1),仅用两个变量记录状态。
七、45. 跳跃游戏 II
题目大意 :给定长度为 n的数组 nums,nums[i]表示从索引 i向后跳的最大长度。初始在索引 0,求到达索引 n-1的最小跳跃次数。保证可以到达终点。
解题思路:贪心算法(分层跳跃)
核心思想是每次跳跃都尽可能扩展"当前能到达的最远边界",直到覆盖终点:
-
初始化
jumps(跳跃次数)为0,current_end(当前跳跃的边界)为0,farthest(当前能到达的最远位置)为0。 -
遍历数组(注意:不需要遍历最后一个元素,因为到达终点后停止):
-
更新
farthest为max(farthest, i + nums[i])(从当前位置i能跳到的最远位置)。 -
若遍历到
current_end(当前跳跃的边界),说明需要再跳一次,更新current_end为farthest,并增加jumps。 -
若
current_end已覆盖终点(current_end >= n-1),提前终止循环。
-
代码实现(C++)
class Solution {
public:
int jump(vector<int>& nums) {
int n = nums.size();
int jumps = 0; // 跳跃次数
int current_end = 0; // 当前跳跃的边界
int farthest = 0; // 当前能到达的最远位置
for (int i = 0; i < n - 1; ++i) { // 不需要遍历最后一个元素
farthest = max(farthest, i + nums[i]); // 更新最远位置
if (i == current_end) { // 到达当前跳跃的边界,需要再跳一次
jumps++;
current_end = farthest; // 更新当前跳跃的边界
if (current_end >= n - 1) { // 已能到达终点,提前终止
break;
}
}
}
return jumps;
}
};
算法特性
-
时间复杂度:O(n),仅需遍历数组一次。
-
空间复杂度:O(1),仅用三个变量记录状态。
八、763. 划分字母区间
题目大意 :给定字符串 s,将其划分为尽可能多的片段,使得同一字母最多出现在一个片段中。返回各片段的长度列表。
解题思路:贪心 + 哈希表记录最后位置
核心思想是先记录每个字符的最后出现位置,再遍历字符串,动态扩展当前片段的右边界,直到覆盖所有字符的最后位置:
-
用哈希表
lastPos记录每个字符最后出现的索引。 -
遍历字符串,维护
start(当前片段的起始位置)和end(当前片段的动态右边界,初始为0):-
对于每个字符
s[i],更新end为max(end, lastPos[s[i]])(确保当前片段包含该字符的最后出现位置)。 -
当
i == end时,说明当前片段已包含所有字符的最后出现位置,记录片段长度(end - start + 1),并更新start为end + 1。
-
代码实现(C++)
class Solution {
public:
vector<int> partitionLabels(string s) {
unordered_map<char, int> lastPos;
// 步骤1:记录每个字符最后出现的位置
for (int i = 0; i < s.size(); ++i) {
lastPos[s[i]] = i;
}
vector<int> result;
int start = 0; // 当前片段的起始位置
int end = 0; // 当前片段的动态右边界
for (int i = 0; i < s.size(); ++i) {
// 扩展当前片段的右边界,确保包含所有字符的最后出现位置
end = max(end, lastPos[s[i]]);
// 当前位置是片段的终点,记录长度并更新起始位置
if (i == end) {
result.push_back(end - start + 1);
start = end + 1; // 下一个片段的起始位置
}
}
return result;
}
};
算法特性
-
时间复杂度:O(n),遍历字符串两次(一次记录最后位置,一次划分片段)。
-
空间复杂度:O(1)(或 O(26),因为字符是小写字母,哈希表最多存26个键值对)。
总结
这三类题目(股票买卖、跳跃游戏、字母划分)都属于贪心算法 的典型应用,核心思想是在每一步选择局部最优,最终得到全局最优。通过动态记录状态(如最小价格、最远跳跃位置、字符最后位置),可以在线性时间内解决问题,空间复杂度也保持在常量级。
后续我们将继续分享更多 LeetCode 热题的解法,涵盖链表、树、动态规划等主题,欢迎持续关注!