贪心算法专题(十四):万流归宗——「合并区间」

哈喽各位,我是前端L。

欢迎来到贪心算法专题第十四篇!

题目很简单:给你一堆乱序的区间,比如 1,3, 8,10, 2,6, 15,18

请你把重叠的区间合并。

  • [1,3][2,6] 重叠,合并后变成 [1,6](取最小起点,最大终点)。

  • [8,10][15,18] 没人理它们,保持原样。

  • 最终输出:[[1,6], [8,10], [15,18]]

力扣 56. 合并区间

https://leetcode.cn/problems/merge-intervals/

题目分析:

  • 输入intervals 数组。

  • 输出:合并后的数组。

核心思维:排序 + 动态更新右边界

依然是老套路:先按 Start 排序。

排序后:1,3, 2,6, 8,10, 15,18

贪心策略:

我们将第一个区间放入结果集 result 中作为**"当前正在合并的区间"**。

然后遍历后面的区间:

  1. 重叠了? (当前.start <= 结果集中最后一个.end)

    • 不需要新增区间。

    • 合并操作 :我们要把结果集里那个区间的右边界撑大。

    • 新的右边界 = Math.max(结果集.end, 当前.end)

    • 注意:这里取 Max,因为我们要尽可能包含更多的范围(合并)。这与"射气球"取 Min(找交集)正好相反。

  2. 没重叠?

    • 说明当前结果集里的那个区间已经合并完毕,无法再向后延伸了。

    • 直接把当前区间 push 进结果集,作为新的"正在合并对象"。

算法流程 (JavaScript)

  1. 特判:长度为 0 或 1 直接返回。

  2. 排序intervals.sort((a, b) => a[0] - b[0])

  3. 初始化result = [intervals[0]]

  4. 遍历 :从 i = 1 开始。

    • 取出 result 的最后一个元素 last

    • 取出当前遍历元素 cur

    • 如果 cur[0] <= last[1] (有重叠):

      • last[1] = Math.max(last[1], cur[1]) (直接修改 result 里的元素)。
    • 否则:

      • result.push(cur)
  5. 返回 result

代码实现

JavaScript

复制代码
/**
 * @param {number[][]} intervals
 * @return {number[][]}
 */
var merge = function(intervals) {
    if (intervals.length === 0) return [];
    
    // 1. 按左边界从小到大排序
    intervals.sort((a, b) => a[0] - b[0]);
    
    // 初始化结果集,先把第一个放进去
    const result = [intervals[0]];
    
    // 2. 从第二个开始遍历
    for (let i = 1; i < intervals.length; i++) {
        let cur = intervals[i];
        // 获取结果集中"当前正在合并"的那个区间(也就是最后一个)
        let last = result[result.length - 1];
        
        // 判断是否重叠
        // 当前区间的左边界 <= 上一个区间的右边界
        if (cur[0] <= last[1]) {
            // 发现重叠!执行合并
            // 只需要更新右边界,左边界肯定是用 last[0] (因为它更小,排序保证的)
            // 右边界要取两者中最大的,这样才能包住两个区间
            last[1] = Math.max(last[1], cur[1]);
        } else {
            // 没有重叠,直接放入结果集
            result.push(cur);
        }
    }
    
    return result;
};

深度辨析:区间三兄弟总结

这三道题(452射气球, 435无重叠, 56合并)是区间问题的"三驾马车"。它们的第一步永远是 Sort by Start

区别在于处理重叠时的贪心决策

题目 目标 重叠时的操作 边界更新策略
452. 射气球 找公共交集 箭数不变,范围变小 end = Math.min (取交集)
435. 无重叠 丢弃区间 移除数+1,保留短的 end = Math.min (为了不挡路)
56. 合并区间 融合区间 区间数不变,范围变大 end = Math.max (取并集)

记住这个表格,区间问题就通关了!

复杂度分析

  • 时间复杂度:O(N log N)

    • 瓶颈在排序。遍历只是 O(N)。
  • 空间复杂度:O(N)

    • 需要一个 result 数组来存储结果(如果那是返回值不算额外空间,则是 O(log N) 的排序栈空间)。

下一题预告:单调递增的数字

区间问题到此结束! 接下来我们要处理一道非常有意思的数字逻辑题

  • 给定一个非负整数 N

  • 找出小于或等于 N 的最大整数,同时这个整数每一位上的数字都要是单调递增的。

  • 比如 N = 10 -> 输出 9

  • 比如 N = 1234 -> 输出 1234

  • 比如 N = 332 -> 输出 299

这道题的贪心策略在于:一旦发现某一位比后面一位大(破坏了单调性),就得把这一位减 1,然后把后面所有位都变成 9。 就像做借位减法一样。

准备好玩数字了吗?下期见!

相关推荐
我命由我123452 小时前
CesiumJS 笔记 - 获取容器中心点、Cartesian3 clone 方法、修改 Cartesian3 对象的高度
前端·javascript·css·前端框架·html·html5·js
大明者省3 小时前
WSL2 Ubuntu22.04 GPU训练环境配置指南
人工智能·算法·计算机视觉
抱抱宝3 小时前
Agent-study项目教程(03):手写 Mini-ReAct Agent(不依赖框架)
javascript·人工智能·gpt·react.js·prompt·agent
用户938515635074 小时前
Next.js 笔记系统(二):Redis 数据服务与侧边栏组件拆分实战
javascript·全栈
白狐_7985 小时前
408数据结构第8章:排序②——性质对比秒杀、场景选择与外部排序
java·数据结构·算法
zander2585 小时前
LeetCode 84:柱状图中的最大矩形——单调栈如何确定左右边界
java·数据结构·算法
Shell运维手记6 小时前
Linux 常用基础命令学习笔记
linux·运维·笔记·学习·算法·github
杨航 AI6 小时前
O(n log n):线性对数原理拆解 这个是排序算法的黄金复杂度之一。
算法·排序算法
船厂电气自动化ai大模型7 小时前
AI大模型与数学 第32课 函数凹凸性与二阶导数:拐点求解、凹凸区间计算(10道二阶导数计算题)
数据结构·人工智能·python·深度学习·算法
旖旎夜光7 小时前
LeetCode 3:无重复字符的最长子串(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口