贪心算法深化 III

56. 合并区间

解题思路

这道题的贪心核心是:先按区间左边界排序,再逐个合并重叠 / 相邻区间(和之前的重叠区间题思路一脉相承)。

  1. 排序:将所有区间按左边界升序排列(确保按顺序处理,避免遗漏)。
  2. 合并区间
    • 初始化结果列表,将第一个区间加入。
    • 遍历后续区间:取出结果列表最后一个区间,判断当前区间是否与它重叠(当前区间左边界 ≤ 最后一个区间右边界)。
      • 重叠:合并区间(更新最后一个区间的右边界为两者最大值)。
      • 不重叠:将当前区间直接加入结果列表。
  3. 最终返回结果列表。

完整 Java 代码

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

class Solution {
    public int[][] merge(int[][] intervals) {
        if (intervals.length <= 1) {
            return intervals; // 0个或1个区间,无需合并
        }
        
        // 步骤1:按区间左边界升序排序
        Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
        
        // 步骤2:初始化结果列表,加入第一个区间
        List<int[]> merged = new ArrayList<>();
        merged.add(intervals[0]);
        
        // 步骤3:遍历并合并后续区间
        for (int i = 1; i < intervals.length; i++) {
            int[] last = merged.get(merged.size() - 1); // 结果列表最后一个区间
            int[] curr = intervals[i]; // 当前遍历的区间
            
            if (curr[0] <= last[1]) {
                // 重叠,合并区间:更新最后一个区间的右边界
                last[1] = Math.max(last[1], curr[1]);
            } else {
                // 不重叠,直接加入结果列表
                merged.add(curr);
            }
        }
        
        // 步骤4:转换为数组返回
        return merged.toArray(new int[merged.size()][]);
    }
}

代码解析

  1. 边界处理:区间数 ≤ 1 时,直接返回原数组(无需合并)。
  2. 排序关键:按左边界升序排序,保证处理顺序是 "从左到右",避免交叉处理导致逻辑混乱。
  3. 合并逻辑
    • List 存储结果,方便动态修改最后一个区间。
    • 每次取结果列表最后一个区间,对比当前区间的左边界:
      • 重叠则更新右边界(取最大值,覆盖 "包含" 或 "部分重叠" 场景);
      • 不重叠则直接添加当前区间。
  4. 结果转换 :将 List 转为二维数组返回,符合题目要求。

738. 单调递增的数字

解题思路

这道题的贪心核心是:从后往前遍历,找到第一个破坏递增的位置,将该位置数字减 1,后续所有数字置为 9(保证最大的单调递增数)。

  1. 预处理:将数字转为字符数组(方便逐位修改)。
  2. 从后往前遍历
    • 记录 "需要置为 9 的起始位置"start = len(初始为数组长度,即无需置 9)。
    • 从倒数第二位开始遍历:若当前位数字 > 后一位数字,说明破坏了递增,将当前位减 1,并更新 start 为当前位 + 1(后续位都要置 9)。
  3. 置 9 操作 :将 start 到数组末尾的所有位置为 9。
  4. 转换为数字返回

完整 Java 代码

java 复制代码
class Solution {
    public int monotoneIncreasingDigits(int n) {
        // 步骤1:转为字符数组,方便逐位处理
        char[] digits = String.valueOf(n).toCharArray();
        int len = digits.length;
        int start = len; // 需要置为9的起始位置
        
        // 步骤2:从后往前遍历,找破坏递增的位置
        for (int i = len - 2; i >= 0; i--) {
            if (digits[i] > digits[i + 1]) {
                // 当前位 > 后一位,破坏递增
                digits[i]--; // 当前位减1
                start = i + 1; // 后续位都要置为9
            }
        }
        
        // 步骤3:将start到末尾的位置为9
        for (int i = start; i < len; i++) {
            digits[i] = '9';
        }
        
        // 步骤4:转换为数字返回
        return Integer.parseInt(new String(digits));
    }
}

代码解析

  1. 字符数组转换:将数字转为字符数组,避免频繁的数学运算(取模、除法),更直观处理每一位。
  2. 从后往前遍历:核心是 "找到第一个破坏递增的位置",从后往前能避免重复处理(比如 332 → 先处理第二位 3>2,减为 2,start=2,最终置 9 得 329)。
  3. 置 9 逻辑start 标记了需要置 9 的起始位置,将这些位置置 9 能保证数字最大(比如 1000 → 遍历到第一位 1>0,减为 0,start=1,置 9 得 999)。
  4. 结果转换:将字符数组转回数字,注意处理前导 0(但题目要求单调递增,前导 0 会被自动处理为有效数字)。

贪心算法核心总结

结合之前所有题目,贪心算法的核心逻辑和解题技巧可总结为以下几点:

1. 贪心的本质

  • 局部最优 → 全局最优:每一步都做出当前看起来最好的选择,无需回溯,最终得到全局最优解。
  • 关键:证明局部最优能推导出全局最优(刷题时可通过 "直觉 + 案例验证",无需严格数学证明)。

2. 贪心的常见解题步骤

  1. 排序:绝大多数贪心题都需要先排序(如重叠区间按边界排序、股票按价格排序、身高队列按身高排序),排序是找到 "局部最优" 的前提。
  2. 找边界 / 基准:确定每一步的 "最优判断标准"(如箭的位置选右边界、合并区间看左边界、单调数字找破坏递增的位置)。
  3. 遍历处理:按排序后的顺序遍历,逐次应用局部最优策略,更新结果或边界。

3. 贪心的典型题型及策略

题型分类 核心策略 代表题目
重叠区间 按边界排序,找最优边界(左 / 右) 452/435/763/56
数组 / 数字处理 优先处理极值(最小负数、最大差值) 1005/738
路径 / 跳跃 维护最远可达位置 55/45
分配 / 找零 优先用大面额 / 保留关键资源 135/860
字符串 记录字符边界,按边界划分 763

4. 贪心的解题技巧

  • 拆解问题:复杂问题拆分为 "单方向处理"(如分发糖果先左后右、重建队列先高后矮),避免两头兼顾导致逻辑混乱。
  • 反向思考:如 "最少删除区间"→ 转化为 "最多保留区间",降低思考难度。
  • 案例验证:用简单案例验证贪心策略是否正确(如 332 → 329,1000 → 999)。

5. 贪心的难点

  • 贪心策略无固定模板,需要多刷题积累 "直觉";
  • 部分题目需结合其他知识点(如二叉树、字符串),但核心仍是 "局部最优";
  • 证明贪心的正确性较难,刷题时只需保证 "案例通过 + 逻辑自洽" 即可。

关键点回顾

  1. 合并区间:先按左边界排序,再逐个合并重叠区间,核心是 "更新右边界"。
  2. 单调递增的数字:从后往前找破坏递增的位置,减 1 后后续置 9,保证数字最大。
  3. 贪心核心:排序找局部最优边界,单方向处理,局部最优推导全局最优。
相关推荐
wen__xvn2 小时前
算法刷题目录
算法
Tisfy2 小时前
LeetCode 1292.元素和小于等于阈值的正方形的最大边长:二维前缀和(无需二分)+抽象速懂的描述
算法·leetcode·职场和发展
ZPC82102 小时前
机械臂urdf
人工智能·算法
郝学胜-神的一滴2 小时前
Python对象的自省机制:深入探索对象的内心世界
开发语言·python·程序人生·算法
期待のcode2 小时前
浅堆深堆与支配树
java·jvm·算法
程序员-King.2 小时前
day156—回溯—组合总和(LeetCode-216)
算法·leetcode·回溯
努力学算法的蒟蒻2 小时前
day60(1.19)——leetcode面试经典150
算法·leetcode·面试
且去填词2 小时前
三色标记法与混合写屏障:Go GC 垃圾回收全流程解析
开发语言·算法·golang·三色标记法·gogc·屏障技术
漫随流水2 小时前
leetcode回溯算法(216.组合总和Ⅲ)
数据结构·算法·leetcode·回溯算法