贪心算法深化 II

452. 用最少数量的箭引爆气球

解题思路

这道题的贪心核心是:按气球右边界排序,尽可能让一支箭射穿更多重叠气球

  1. 排序:将所有气球按右边界升序排列(关键!优先处理右边界小的气球,能最大化后续重叠可能性)。
  2. 遍历射箭
    • 初始化第一支箭的位置为第一个气球的右边界。
    • 遍历后续气球:若当前气球的左边界 > 箭的位置,说明无法用当前箭射中,需要新增一支箭,并更新箭的位置为当前气球的右边界;否则,当前箭可射中该气球,无需操作。
  3. 最终箭的总数即为答案。

完整 Java 代码

java 复制代码
import java.util.Arrays;

class Solution {
    public int findMinArrowShots(int[][] points) {
        if (points.length == 0) {
            return 0;
        }
        
        // 步骤1:按气球右边界升序排序(注意用Long避免int溢出)
        Arrays.sort(points, (a, b) -> {
            if (a[1] > b[1]) return 1;
            else if (a[1] < b[1]) return -1;
            else return 0;
        });
        
        // 步骤2:初始化箭的数量和第一支箭的位置
        int arrows = 1;
        long arrowPos = points[0][1]; // 用long防止溢出
        
        // 步骤3:遍历气球,判断是否需要新增箭
        for (int i = 1; i < points.length; i++) {
            // 当前气球的左边界 > 箭的位置,需要新增箭
            if (points[i][0] > arrowPos) {
                arrows++;
                arrowPos = points[i][1];
            }
            // 否则,当前箭可射中该气球,无需操作
        }
        
        return arrows;
    }
}

代码解析

  1. 边界处理:若气球数组为空,直接返回 0。
  2. 排序关键 :按右边界升序排序,这是贪心的核心 ------ 射穿右边界最小的气球,能覆盖最多后续重叠气球;注意用 Long 存储箭的位置,避免 int 溢出(题目中坐标范围可能很大)。
  3. 遍历射箭:初始箭数为 1(至少需要一支箭),箭的位置为第一个气球的右边界;遍历后续气球,若气球左边界超过箭的位置,说明无法覆盖,新增箭并更新箭的位置为当前气球右边界。

435. 无重叠区间

解题思路

这道题的贪心核心是:按区间右边界排序,优先保留右边界小的区间,最大化剩余空间(本质是 "最少删除 = 总数 - 最多保留")。

  1. 排序:将区间按右边界升序排列。
  2. 统计最多可保留的不重叠区间数
    • 初始化保留数为 1,上一个保留区间的右边界为第一个区间的右边界。
    • 遍历后续区间:若当前区间的左边界 ≥ 上一个区间的右边界,说明不重叠,保留数 + 1,并更新上一个区间的右边界;否则跳过(该区间需删除)。
  3. 计算删除数:总区间数 - 最多保留数。

完整 Java 代码

java 复制代码
import java.util.Arrays;

class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        if (intervals.length == 0) {
            return 0;
        }
        
        // 步骤1:按区间右边界升序排序
        Arrays.sort(intervals, (a, b) -> {
            return a[1] - b[1];
        });
        
        // 步骤2:统计最多可保留的不重叠区间数
        int keep = 1; // 至少保留1个区间
        int lastRight = intervals[0][1]; // 上一个保留区间的右边界
        
        for (int i = 1; i < intervals.length; i++) {
            // 当前区间左边界 ≥ 上一个右边界,不重叠,保留
            if (intervals[i][0] >= lastRight) {
                keep++;
                lastRight = intervals[i][1];
            }
        }
        
        // 步骤3:需要删除的数量 = 总数 - 保留数
        return intervals.length - keep;
    }
}

代码解析

  1. 边界处理:区间数组为空时返回 0。
  2. 排序逻辑:按右边界升序排序,这是贪心的关键 ------ 保留右边界小的区间,能给后续区间留出更多空间,从而保留更多不重叠区间。
  3. 统计保留数:初始保留数为 1(第一个区间),遍历后续区间,只要不与上一个保留区间重叠,就保留并更新右边界。
  4. 计算删除数:总区间数减去最多保留数,即为最少需要删除的区间数。

763. 划分字母区间

解题思路

这道题看似是字符串问题,实则是重叠区间的变形 ,贪心核心是:先记录每个字母最后出现的位置,再找当前区间的最大右边界

  1. 预处理:遍历字符串,记录每个字母最后一次出现的索引(相当于每个字母的 "区间右边界")。
  2. 划分区间
    • 初始化当前区间的左边界 start = 0,当前区间的最大右边界 end = 0
    • 遍历字符串:
      • 更新 end 为 "当前字母最后出现位置" 和 "当前 end" 的最大值(扩展当前区间的右边界)。
      • 若遍历到的索引 == end,说明当前区间的所有字母都只在该区间内出现,记录区间长度(end - start + 1),并更新 start = end + 1 开始下一个区间。
  3. 最终返回所有区间长度的列表。

完整 Java 代码

java 复制代码
import java.util.ArrayList;
import java.util.List;

class Solution {
    public List<Integer> partitionLabels(String s) {
        List<Integer> result = new ArrayList<>();
        // 步骤1:记录每个字母最后出现的索引(ASCII码映射)
        int[] lastIndex = new int[26];
        for (int i = 0; i < s.length(); i++) {
            lastIndex[s.charAt(i) - 'a'] = i;
        }
        
        // 步骤2:划分区间
        int start = 0; // 当前区间左边界
        int end = 0;   // 当前区间最大右边界
        
        for (int i = 0; i < s.length(); i++) {
            // 更新当前区间的最大右边界
            end = Math.max(end, lastIndex[s.charAt(i) - 'a']);
            // 遍历到区间右边界,记录长度
            if (i == end) {
                result.add(end - start + 1);
                start = end + 1; // 重置左边界,开始下一个区间
            }
        }
        
        return result;
    }
}

代码解析

  1. 预处理最后索引:用长度为 26 的数组(对应 26 个小写字母)记录每个字母最后出现的索引,时间复杂度 O (n)。
  2. 划分区间核心
    • start 标记当前区间的起始位置,end 标记当前区间的最大右边界(确保区间内所有字母的最后出现位置都在 end 内)。
    • 遍历到 i == end 时,说明当前区间内的所有字母都不会出现在后续位置,此时划分区间并记录长度。
  3. 结果返回:将所有区间长度存入列表并返回,整体时间复杂度 O (n),空间复杂度 O (1)(仅用固定长度的数组)。

总结

  1. 用最少箭引爆气球:贪心核心是「按右边界排序,一箭射穿最多重叠气球」,关键是选右边界最小的位置射箭。
  2. 无重叠区间:贪心核心是「按右边界排序,保留最多不重叠区间」,最少删除数 = 总数 - 最多保留数。
  3. 划分字母区间:贪心核心是「先记录字母最后位置,再找当前区间最大右边界」,本质是字符串版的重叠区间划分。
相关推荐
zsffuture2 小时前
RKNN 8位量化全解析:算法差异与粒度选择实战指南
算法
Pluchon2 小时前
硅基计划4.0 算法 动态规划入门
java·数据结构·算法·动态规划
玄冥剑尊2 小时前
贪心算法深化 III
算法·贪心算法
wen__xvn2 小时前
算法刷题目录
算法
Tisfy2 小时前
LeetCode 1292.元素和小于等于阈值的正方形的最大边长:二维前缀和(无需二分)+抽象速懂的描述
算法·leetcode·职场和发展
ZPC82102 小时前
机械臂urdf
人工智能·算法
郝学胜-神的一滴2 小时前
Python对象的自省机制:深入探索对象的内心世界
开发语言·python·程序人生·算法
期待のcode2 小时前
浅堆深堆与支配树
java·jvm·算法
程序员-King.2 小时前
day156—回溯—组合总和(LeetCode-216)
算法·leetcode·回溯