10分钟搞定合并区间问题:从LeetCode高频面试题到实战应用
你是否曾在处理时间安排、日程管理时遇到过区间重叠的问题?比如将多个会议时间合并为连续的时间段,或者在行程规划中合并重叠的行程安排。这些实际问题背后,其实都隐藏着一个经典的算法问题——合并区间(Merge Intervals)。本文将带你从问题分析到代码实现,彻底掌握这一高频面试题的解决方法。
读完本文,你将能够:
- 理解合并区间问题的核心思路
- 掌握贪心算法在区间问题中的应用
- 独立实现合并区间的完整代码
- 了解插入区间与合并区间的关联与区别
问题定义与示例
合并区间问题可以描述为:给定一组区间,合并所有重叠的区间。例如,给定区间[1,3],[2,6],[8,10],[15,18],合并后的结果应为[1,6],[8,10],[15,18]。
这个问题在实际生活中有很多应用场景,比如:
- 日程安排:合并重叠的会议时间
- 资源分配:合并重叠的资源使用时段
- 数据处理:合并时间序列中的连续数据段
在LeetCode中,合并区间问题对应题号为56,属于中等难度题目,也是面试中的高频考点。
算法思路分析
解决合并区间问题的关键在于以下几个步骤:
-
排序区间:首先按照区间的起始位置对所有区间进行排序,这样可以确保我们按顺序处理区间,避免遗漏重叠情况。
-
遍历合并:从头开始遍历排序后的区间,对于每个区间,判断它是否与前一个已合并的区间重叠。如果重叠,则合并这两个区间;如果不重叠,则将当前区间加入结果列表。
算法流程图
代码实现
下面是基于上述思路的C++实现代码,来自项目中的C++/chapImplement.tex文件:
struct Interval {
int start;
int end;
Interval() : start(0), end(0) { }
Interval(int s, int e) : start(s), end(e) { }
};
//LeetCode, Merge Interval
//复用Insert Intervals的解法
// 时间复杂度O(n log n),空间复杂度O(1)
class Solution {
public:
vector<Interval> merge(vector<Interval> &intervals) {
vector<Interval> result;
if (intervals.empty()) return result;
// 按照区间起始位置排序
sort(intervals.begin(), intervals.end(), [](const Interval& a, const Interval& b) {
return a.start < b.start;
});
result.push_back(intervals[0]);
for (int i = 1; i < intervals.size(); i++) {
Interval& last = result.back();
if (intervals[i].start <= last.end) {
// 重叠,合并区间
last.end = max(last.end, intervals[i].end);
} else {
// 不重叠,添加新区间
result.push_back(intervals[i]);
}
}
return result;
}
};
关键步骤解析
1. 区间结构定义
代码中首先定义了一个Interval结构体来表示区间,包含起始位置start和结束位置end两个成员变量。
2. 排序区间
使用C++标准库的sort函数对区间进行排序,排序的依据是区间的起始位置start。这里使用了Lambda表达式作为排序的比较函数。
3. 合并过程
- 首先将第一个区间加入结果列表
- 对于后续每个区间,与结果列表中最后一个区间比较
- 如果当前区间的起始位置小于等于最后一个区间的结束位置,则合并这两个区间
- 否则,将当前区间直接加入结果列表
插入区间问题
合并区间问题有一个相关的变体问题——插入区间(Insert Interval)。该问题要求在一组非重叠的区间中插入一个新的区间,并合并可能重叠的区间。
项目中同样提供了插入区间问题的解决方案,详见C++/chapImplement.tex文件:
//LeetCode, Insert Interval
// 时间复杂度O(n),空间复杂度O(1)
class Solution {
public:
vector<Interval> insert(vector<Interval> &intervals, Interval newInterval) {
vector<Interval>::iterator it = intervals.begin();
while (it != intervals.end()) {
if (newInterval.end < it->start) {
intervals.insert(it, newInterval);
return intervals;
} else if (newInterval.start > it->end) {
it++;
continue;
} else {
newInterval.start = min(newInterval.start, it->start);
newInterval.end = max(newInterval.end, it->end);
it = intervals.erase(it);
}
}
intervals.insert(intervals.end(), newInterval);
return intervals;
}
};
插入区间问题可以作为合并区间问题的一个子过程,通过反复调用插入区间函数,也可以实现合并区间的功能。
算法复杂度分析
-
时间复杂度:O(n log n),主要来自于对区间的排序操作,其中n是区间的数量。排序后的遍历过程复杂度为O(n)。
-
空间复杂度:O(1)或O(n),如果排序算法使用的是原地排序(如快速排序),则空间复杂度为O(1);如果考虑结果列表所占用的空间,则为O(n)。
实际应用举例
假设我们有以下会议时间需要合并:
[[9,10], [1,3], [2,4], [5,7], [8,9]]
按照我们的算法,首先对这些区间进行排序:
[[1,3], [2,4], [5,7], [8,9], [9,10]]
然后进行合并:
- [1,3]和[2,4]合并为[1,4]
- [5,7]不重叠,直接加入
- [8,9]和[9,10]合并为[8,10]
最终合并结果为:
[[1,4], [5,7], [8,10]]
总结与扩展
合并区间问题是一个经典的贪心算法应用案例,通过排序和一次遍历即可高效解决。掌握这一问题的解决思路,有助于我们处理各种实际场景中的区间重叠问题。
项目中还提供了更多相关算法问题的解决方案,如:
这些问题都涉及到了类似的区间处理或数组操作技巧,值得深入学习和研究。
通过本文的学习,相信你已经掌握了合并区间问题的核心解决方法。在实际面试中,面试官可能会对此问题进行变形或扩展,例如要求合并多个类型的区间,或者在特定约束下进行区间合并。此时,我们需要灵活运用本文所学的基本思路,结合具体问题进行分析和解决。
最后,建议你尝试自己实现一遍合并区间算法,并思考是否有其他可能的优化方法或不同的解决思路。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



