小哆啦解题记 · 射箭引爆气球,怎么射最省箭?

💥题目:452. 用最少数量的箭引爆气球 - 力扣(LeetCode)

🎯目标:给定一堆气球区间 points = [[x1, y1], [x2, y2], ...],每支箭垂直发射,穿越 x 轴,求最少几支箭能引爆全部气球。


🐣 第一招:暴力开干,能炸一个是一个

小哆啦最初的思路是"能合就合",于是他选择了从头到尾扫描所有区间,遇到能重叠的就合起来。

ini 复制代码
function findMinArrowShots(points: number[][]): number {
    if (points.length === 0) return 0;

    points.sort((a, b) => a[0] - b[0]); // 先按起点排序
    const merged: number[][] = [];

    for (let i = 0; i < points.length; i++) {
        let [start, end] = points[i];

        // 尝试与merged中已有的区间合并
        let mergedFlag = false;
        for (let j = 0; j < merged.length; j++) {
            let [mStart, mEnd] = merged[j];
            if (start <= mEnd) { // 有重叠
                merged[j][0] = Math.max(mStart, start);
                merged[j][1] = Math.min(mEnd, end); // 缩小重叠区
                mergedFlag = true;
                break;
            }
        }

        if (!mergedFlag) {
            merged.push([start, end]);
        }
    }

    return merged.length;
}

🪦 问题 :暴力方法虽然思路直白,但每次都去线性查找可以合并的区间,最坏时间复杂度是 O(n^2)。一旦数据量大,性能"炸裂",不是炸气球,是炸程序了......


🔁 第二招:使用 reduce 合并区间

小哆啦忽然想起前几天刚学的 reduce,想来练练手,于是有了👇

ini 复制代码
function findMinArrowShots(points: number[][]): number {
    if (points.length === 0) return 0;

    points.sort((a, b) => a[0] - b[0]); // 按起点排序

    const merged = points.reduce((acc: number[][], cur: number[]) => {
        const last = acc[acc.length - 1];
        if (last && cur[0] <= last[1]) {
            // 有重叠,取最小交集
            acc[acc.length - 1] = [last[0], Math.min(last[1], cur[1])];
        } else {
            acc.push(cur);
        }
        return acc;
    }, []);

    return merged.length;
}

🧠 思路核心:

  • 排序后,所有气球按起点递增排列。
  • 遇到区间重叠(cur[0] <= last[1]),说明可以一箭双雕,我们只保留交集 [start, min(end1, end2)]。
  • 统计多少个独立的交集区间,就是箭的数量。

💡 优点 :

结构清晰,代码优雅,掌握了 reduce 的你可以熟练操作集合合并类问题。

⛔️ 缺点 :

虽然没超时,但它并不是最优的贪心策略 ,因为合并的是"最小交集",但没有利用"最早结束"的贪心性质。


🎯 第三招(推荐):经典贪心 · 选最早结束的射爆区间

ini 复制代码
function findMinArrowShots(points: number[][]): number {
    if (points.length === 0) return 0;

    // 按右端点升序排序
    points.sort((a, b) => a[1] - b[1]);

    let arrows = 1;
    let end = points[0][1];

    for (let i = 1; i < points.length; i++) {
        const [start, currEnd] = points[i];

        if (start > end) {
            arrows++;         // 当前气球不在上次箭的覆盖范围
            end = currEnd;    // 发一支新箭,目标是当前气球的右边界
        }
        // 否则:被当前这支箭顺带炸掉了,什么都不用干
    }

    return arrows;
}

🧠 贪心的关键点在这里:

"只要当前气球的起点大于上一支箭能射到的范围,那就必须射新箭;否则,上一支箭还能炸到这个气球。"

🎯 为什么排序右端点是关键?

  • 排序后,每次选择"最早结束"的气球优先射,这样能让当前这支箭尽可能炸到后续更多气球。
  • 这是典型的**"区间选点贪心"模型**,思路类似"会议安排问题"。

🧪 三种方案对比分析表

比较项 暴力合并 reduce合并 贪心策略(推荐)
时间复杂度 O(n^2) O(n log n) O(n log n)
算法思想 逐个比较合并 合并重叠取交集 尽可能让一箭炸更多
可读性 中 强 强
是否使用贪心 ❌ ❌ ✅
算法效率 ❌ 慢 ✅ 稳定 ✅✅ 最优
可拓展性 中 中 高

✅ 总结一下,小哆啦的最终选择:

"reduce 虽然帅,但炸气球还是要靠贪心!"

  • 🌪 当你想写优雅代码练手,reduce 合并思路非常棒;
  • 🧠 当你想追求最优性能,贪心策略才是射箭的"王道"。
相关推荐
倒头就睡的小比特3 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼3 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
旖旎夜光3 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark3 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her13 天前
并查集-听课笔记
笔记·算法·并查集
码流子3 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven3 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark3 天前
从算法设计模式看编程思维的抽象能力4
算法
2601_962218613 天前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法