在刷算法时,有两类题非常高频:连续子数组相关问题 和 区间处理问题。本文将通过两道典型题目来理解它们的本质思路与代码实现。
一、最大子数组和(Maximum Subarray)
题目
给你一个整数数组 nums,请找出一个具有最大和的连续子数组(至少包含一个元素),返回其最大和。
思路解析
采用 前缀和 + 最小前缀和 的思想(将它转化为买卖股票机问题):
当前前缀和 - 历史最小前缀和 = 当前能得到的最大连续子数组和
-
用
pre_sum表示当前的前缀和 -
用
pre_sum_min记录遇到的最小前缀和 -
每次更新结果
ans = max(ans, pre_sum - pre_sum_min) -
不断更新
pre_sum_min
这就像"低买高卖股票问题":
前面最低的时候买入,现在卖出,赚得最多
✔代码实现
cpp
class Solution {
public:
int maxSubArray(vector<int>& nums) {
// 思路: 利用前缀和 将其转化为一个买卖股票机的问题
int ans = INT_MIN;
int pre_sum_min = 0;
int pre_sum = 0;
for (int x : nums) {
pre_sum += x;
ans = max(ans, pre_sum - pre_sum_min);
pre_sum_min = min(pre_sum, pre_sum_min);
}
return ans;
}
};
复杂度分析
| 项目 | 数值 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(1) |
| 思想核心 | 前缀和 & 动态更新最优 |
二、合并区间(Merge Intervals)
题目
给定一组区间,将重叠区间合并为一个。
思路解析
基本策略:
-
先按区间起点排序
-
维护当前合并区间
[start, end] -
遍历过程中判断是否重叠
-
如果重叠:更新
end = max(end, 当前区间的end) -
如果不重叠:将已有区间压入结果,新建区间
-
注意:最后一个区间需要手动加入答案。
✔代码实现
cpp
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
// 思路: 先排序,然后通过start 和 end 比较更新区间,加入答案,别忘了最后单独加入,因为最后构成不了不符合if的条件
vector<vector<int>> ans;
sort(intervals.begin(), intervals.end());
int start = intervals[0][0];
int end = intervals[0][1];
for (int i = 1; i < intervals.size(); ++i) {
if (intervals[i][0] > end) {
ans.push_back({start, end});
start = intervals[i][0];
end = intervals[i][1];
}
else {
end = max(end,intervals[i][1]);
}
}
ans.push_back({start, end});
return ans;
}
};
复杂度分析
| 项目 | 数值 |
|---|---|
| 排序开销 | O(n log n) |
| 遍历开销 | O(n) |
| 总复杂度 | O(n log n) |
| 技巧核心 | 排序 + 贪心合并 |
总结 & 对比
| 题目 | 核心策略 | 是否排序 | 是否贪心 | 难点 |
|---|---|---|---|---|
| 最大子数组和 | 前缀和 & 动态更新最低点 | 否 | 否 | 把连续子数组转成差值最大问题 |
| 合并区间 | 排序 + 贪心扩展区间 | 是 | 是 | 细节处理 & 最后区间别漏 |
小结
最大子数组和 ------ "找最低点买入,当前卖出"
合并区间 ------ "先排好队,再合并重叠的人"