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

💥题目: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 合并思路非常棒;
  • 🧠 当你想追求最优性能,贪心策略才是射箭的"王道"。
相关推荐
ZhengEnCi1 小时前
大模型的分类到底怎么分:从基础模型到推理模型、从通用到专用的完整图谱
算法
取名真是2 小时前
Python 算法知识点从基础到进阶
开发语言·python·算法
罗西的思考2 小时前
【轻量化复刻大模型 / KIMI K3】KDA 模块交互式演示
人工智能·算法·机器学习
nm_zfyjt4 小时前
信息流广告投放优化实践:线索成本评估模型与转化链路重构(家装建材案例)
算法·重构·内蒙古朋友圈广告服务商·内蒙古朋友圈获客
w_zero_one6 小时前
算法--构造
java·数据结构·算法
其实防守也摸鱼6 小时前
信创安全竞赛知识总结与题库
大数据·网络·人工智能·算法·安全·php
小超人诞生日记7 小时前
C++ 智能指针的使用及其原理详解
c++·算法
AiNightVision11 小时前
无人机夜视模块如何选择?——从参数指标到系统集成的完整选型指南
人工智能·算法·计算机视觉·车载系统·无人机·视频·智能硬件
牧羊人.33312 小时前
卷积神经网络之VGG
人工智能·深度学习·神经网络·算法·cnn