LeetCode 56合并区间:排序与贪心

一、 题目来源与描述

题目来源:LeetCode 第 56 题 - 合并区间 (Merge Intervals)

难度:中等

题目描述 :

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervalsi = starti, endi。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。

二、 输入数据结构深度解析

在解答本题前,我们需要明确输入数据的结构。题目给出的输入 intervals 其实是一个 二维数组(在 C++ 中为二维向量 vector<vector<int>>)。

外层维度:表示有多少个区间。例如 intervals.size() 代表区间的总个数。

内层维度:固定长度为 2。intervalsi0 代表第 i 个区间的左端点(起始位置),intervalsi1 代表第 i 个区间的右端点(结束位置)。

示例解析 :

输入:intervals = \[1,3,2,6,8,10,15,18]

这代表集合中有 4 个区间:

  1. 区间 1:从 1 到 3
  2. 区间 2:从 2 到 6
  3. 区间 3:从 8 到 10
  4. 区间 4:从 15 到 18

三、 核心算法思路:排序 + 贪心

这道题如果直接两两比较,时间复杂度会非常高。最优的解法基于一个关键的预处理步骤:排序。

1. 为什么需要排序?

如果区间是无序的,比如 \[8,10, 1,3, 2,6],我们很难判断 1,3 和 2,6 是否重叠,因为它们不相邻。但如果我们按区间的左端点 进行升序排序,数组就会变成 \[1,3, 2,6, 8,10]。

此时,我们只需要从左到右遍历一次,比较当前区间 与前一个已合并区间的关系即可。

2. 贪心策略与合并逻辑

排序后,我们维护一个结果数组 merged。遍历排序后的区间,对于每一个当前区间 curr,与 merged 中的最后一个区间 last 进行比较:

情况 A:发生重叠(或相接)

如果 curr 的左端点 ≤ last 的右端点(即 curr0 <= last1),说明两个区间有交集。

操作 :更新 last 的右端点,取两者右端点的最大值:last1 = max(last1, curr1)。

( 注意:这里不需要更新左端点,因为我们已经按左端点排序, last的左端点一定小于等于 curr的左端点)

情况 B:没有重叠

如果 curr 的左端点 > last 的右端点(即 curr0 > last1),说明两个区间完全分离。

操作:直接将 curr 加入 merged 数组,成为新的 last。

3. 算法流程图解

以 intervals = \[1,3,2,6,8,10,15,18] 为例:

排序:已经是升序。

初始化:merged = \[1,3]

遍历2,6:2 <= 3 (重叠) -> 更新 merged 末尾为 1, max(3,6) = 1,6。此时 merged = \[1,6]

遍历8,10:8 > 6 (不重叠) -> 直接加入。此时 merged = \[1,6, 8,10]

遍历15,18:15 > 10 (不重叠) -> 直接加入。此时 merged = \[1,6, 8,10, 15,18]

结束,返回 merged。

四、 C++ 代码实现

cpp 复制代码
class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        if (intervals.empty()) return {};
        sort(intervals.begin(), intervals.end());
        vector<vector<int>> merged;
        merged.push_back(intervals[0]);
        for (int i = 1; i < intervals.size(); ++i) {
            int last_right = merged.back()[1];
            int curr_left = intervals[i][0];
            int curr_right = intervals[i][1];
            if (curr_left <= last_right) {
                merged.back()[1] = max(last_right, curr_right);
            } else {
                merged.push_back(intervals[i]);
            }
        }
        return merged;
    }
};

五、 复杂度分析

时间复杂度: O( N logN)

主要消耗在排序上,C++ 的 std::sort 平均时间复杂度为 O( N logN)。

遍历合并的过程只需要一次线性扫描,时间复杂度为 O(N)。

总体时间复杂度为 O( N logN) ,其中 N是区间的数量。

空间复杂度: O( logN) 或 O(N)

如果不考虑返回结果所占用的空间,主要取决于排序算法的递归栈空间,通常为 O( logN)。

如果考虑返回结果 merged 数组,最坏情况下(所有区间都不重叠)需要存储 N 个区间,空间复杂度为 O(N)。

相关推荐
程序员老陆1 小时前
C++ 继承方式:公有、保护、私有,到底在控制什么
开发语言·c++
坤坤子吖1 小时前
C++11——Lambda、function、bind 与可调用对象
c++·笔记·学习
白杨尚青2 小时前
C++入门篇(十一):string(中)——容量与增删改查:一篇吃透所有常用接口(万字详解)
java·开发语言·c++
学术小李2 小时前
cudaMalloc第一个参数为何是指针的指针
算法
kukubuzai2 小时前
二分查找系列一
数据结构·算法·leetcode
老歌老听老掉牙2 小时前
动态规划:从递归到最优决策的算法艺术
算法·动态规划
朔北之忘 Clancy2 小时前
2024 CSP-J 第二轮真题解析
c++·算法·青少年编程·noip·csp·信奥赛·noi
Lazionr2 小时前
set和map的使用——从基础接口到实际应用
c++