10分钟搞定合并区间问题:从LeetCode高频面试题到实战应用

10分钟搞定合并区间问题:从LeetCode高频面试题到实战应用

【免费下载链接】leetcode LeetCode题解,151道题完整版。广告:推荐刷题网站 https://www.lintcode.com/?utm_source=soulmachine 【免费下载链接】leetcode 项目地址: https://gitcode.com/gh_mirrors/leet/leetcode

你是否曾在处理时间安排、日程管理时遇到过区间重叠的问题?比如将多个会议时间合并为连续的时间段,或者在行程规划中合并重叠的行程安排。这些实际问题背后,其实都隐藏着一个经典的算法问题——合并区间(Merge Intervals)。本文将带你从问题分析到代码实现,彻底掌握这一高频面试题的解决方法。

读完本文,你将能够:

  • 理解合并区间问题的核心思路
  • 掌握贪心算法在区间问题中的应用
  • 独立实现合并区间的完整代码
  • 了解插入区间与合并区间的关联与区别

问题定义与示例

合并区间问题可以描述为:给定一组区间,合并所有重叠的区间。例如,给定区间[1,3],[2,6],[8,10],[15,18],合并后的结果应为[1,6],[8,10],[15,18]

这个问题在实际生活中有很多应用场景,比如:

  • 日程安排:合并重叠的会议时间
  • 资源分配:合并重叠的资源使用时段
  • 数据处理:合并时间序列中的连续数据段

在LeetCode中,合并区间问题对应题号为56,属于中等难度题目,也是面试中的高频考点。

算法思路分析

解决合并区间问题的关键在于以下几个步骤:

  1. 排序区间:首先按照区间的起始位置对所有区间进行排序,这样可以确保我们按顺序处理区间,避免遗漏重叠情况。

  2. 遍历合并:从头开始遍历排序后的区间,对于每个区间,判断它是否与前一个已合并的区间重叠。如果重叠,则合并这两个区间;如果不重叠,则将当前区间加入结果列表。

算法流程图

mermaid

代码实现

下面是基于上述思路的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]]

总结与扩展

合并区间问题是一个经典的贪心算法应用案例,通过排序和一次遍历即可高效解决。掌握这一问题的解决思路,有助于我们处理各种实际场景中的区间重叠问题。

项目中还提供了更多相关算法问题的解决方案,如:

这些问题都涉及到了类似的区间处理或数组操作技巧,值得深入学习和研究。

通过本文的学习,相信你已经掌握了合并区间问题的核心解决方法。在实际面试中,面试官可能会对此问题进行变形或扩展,例如要求合并多个类型的区间,或者在特定约束下进行区间合并。此时,我们需要灵活运用本文所学的基本思路,结合具体问题进行分析和解决。

最后,建议你尝试自己实现一遍合并区间算法,并思考是否有其他可能的优化方法或不同的解决思路。

【免费下载链接】leetcode LeetCode题解,151道题完整版。广告:推荐刷题网站 https://www.lintcode.com/?utm_source=soulmachine 【免费下载链接】leetcode 项目地址: https://gitcode.com/gh_mirrors/leet/leetcode

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值