二刷 LeetCode:两道经典贪心题复盘

目录

[一、LeetCode 45. 跳跃游戏 II](#一、LeetCode 45. 跳跃游戏 II)

题目回顾

核心思路(正向贪心)

[Java 实现代码](#Java 实现代码)

二刷反思

[二、LeetCode 763. 划分字母区间](#二、LeetCode 763. 划分字母区间)

题目回顾

[核心思路(两次遍历 + 边界扩展)](#核心思路(两次遍历 + 边界扩展))

[Java 实现代码](#Java 实现代码)

二刷反思

三、贪心算法的通用复盘


二刷贪心算法专题,把两道经典中等题「跳跃游戏 II」和「划分字母区间」重新啃了一遍。比起第一次的懵懵懂懂,这次明显对贪心的核心思想有了更透彻的理解,也想把复盘整理成博客,帮自己沉淀下来,也给同在路上的伙伴们一点参考。


一、LeetCode 45. 跳跃游戏 II

题目回顾

给定一个非负整数数组 nums,你最初位于数组的第一个下标。数组中的每个元素代表你在该位置可以跳跃的最大长度。你的目标是使用最少的跳跃次数到达数组的最后一个位置。题目保证你一定可以到达终点。

核心思路(正向贪心)

这道题的贪心核心是:不需要知道每一步具体跳到哪个位置,只需要维护当前跳跃能覆盖的区间,在区间内找到下一步能跳得最远的位置,以此减少跳跃次数。

  • 维护三个变量:
    • step:记录跳跃次数,初始为 0
    • currentEnd:当前这一跳能到达的最远边界
    • maxReach:在当前区间内遍历,更新能到达的下一个最远边界
  • 遍历数组(注意不需要遍历到最后一个元素):
    1. 更新 maxReach = max(maxReach, i + nums[i])
    2. 当遍历到 currentEnd 时,说明当前区间已经遍历完毕,必须进行下一次跳跃:step++,同时更新 currentEnd = maxReach
    3. 如果 currentEnd 已经能到达终点,直接提前结束

Java 实现代码

java

运行

复制代码
public int jump(int[] nums) {
    int n = nums.length;
    if (n <= 1) return 0;
    int step = 0;
    int currentEnd = 0;
    int maxReach = 0;
    for (int i = 0; i < n - 1; i++) {
        maxReach = Math.max(maxReach, i + nums[i]);
        if (i == currentEnd) {
            step++;
            currentEnd = maxReach;
            if (currentEnd >= n - 1) break;
        }
    }
    return step;
}

二刷反思

  • 第一次写的时候,很容易陷入「找具体路径」的误区,想着用动态规划记录每个位置的最少跳跃次数,结果写出来的代码又复杂又低效。
  • 这次才真正理解:贪心的精髓是放弃局部最优路径的细节,只保留全局最优的边界信息。时间复杂度直接从 O (n²) 降到了 O (n),空间复杂度 O (1),完美符合面试官的期望。
  • 易错点:循环条件是 i < n - 1,因为最后一个位置不需要再跳一次,否则会多算一次跳跃次数。

二、LeetCode 763. 划分字母区间

题目回顾

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。

核心思路(两次遍历 + 边界扩展)

这道题的贪心核心是:先预处理每个字符的最后出现位置,再遍历字符串动态扩展当前片段的边界,当遍历到边界时,就是一个合法的片段。

  1. 预处理:第一次遍历字符串,用数组 last[26] 记录每个小写字母最后一次出现的下标
  2. 贪心划分:第二次遍历字符串,维护当前片段的起点 start 和终点 end
    • 对于每个字符 c,更新 end = max(end, last[c - 'a'])
    • 当 i == end 时,说明当前片段已经包含了所有出现过的字符,记录片段长度 end - start + 1,并更新 start = i + 1

Java 实现代码

java

运行

复制代码
public List<Integer> partitionLabels(String s) {
    List<Integer> res = new ArrayList<>();
    int[] last = new int[26];
    int n = s.length();
    // 预处理每个字符的最后出现位置
    for (int i = 0; i < n; i++) {
        last[s.charAt(i) - 'a'] = i;
    }
    int start = 0;
    int end = 0;
    for (int i = 0; i < n; i++) {
        end = Math.max(end, last[s.charAt(i) - 'a']);
        if (i == end) {
            res.add(end - start + 1);
            start = i + 1;
        }
    }
    return res;
}

二刷反思

  • 第一次做的时候,想到了用哈希表记录最后位置,但对「动态扩展边界」的理解不够深,差点写了区间合并的复杂逻辑。
  • 这次才明白:贪心的关键是,当前片段的边界由已出现字符的最远位置决定,不需要额外处理区间合并,遍历过程中自然就能划分出所有合法片段。
  • 易错点:边界更新时,要一直取当前字符的最远位置,而不是只更新一次,否则会漏掉后面出现的字符。

三、贪心算法的通用复盘

这两道题看似场景不同,但底层的贪心思想高度一致:

  1. 放弃局部细节,聚焦全局最优:不纠结每一步的具体选择,只维护能影响后续决策的关键边界信息
  2. 预处理 + 单次遍历:先通过一次遍历拿到关键信息(比如最远位置、最大边界),再用一次遍历完成贪心决策,时间复杂度稳定在 O (n)
  3. 边界意识:两道题都对边界的处理有严格要求,比如跳跃游戏的循环终止条件、划分字母区间的片段终点判断,边界错一步,结果就会出错。
相关推荐
Dream it possible!2 小时前
LeetCode 面试经典 150_多维动态规划_交错字符串(97_C++_中等)
算法·leetcode·面试·动态规划
心抵鹊3 小时前
算法之双指针系列(一)
算法·leetcode
十二3 小时前
【洛谷题解】P1083 [NOIP 2012 提高组] 借教室(二分答案 + 差分)
数据结构·c++·算法·蓝桥杯
小此方4 小时前
[算法实战论:动态规划]回文串问题的dp解法,回文串问题的母题,以及以此为基础的派生题
算法·动态规划
Pniubi4 小时前
力扣560.和为k的子数组
算法·leetcode·职场和发展
D e re k ZH5 小时前
C++ 从零入门(一):基础语法与程序结构
数据结构·c++·算法
Pniubi5 小时前
力扣438.找到字符串中所有字母异位词
算法·leetcode·职场和发展
東隅已逝,桑榆非晚8 小时前
算法6. 双指针法-查找总价格为目标的两个商品
c++·学习·算法
Eyfcom8 小时前
〖物联网终端实战 01〗UWB 室内高精度定位:仓储 AGV 与人员的厘米级轨迹追踪
物联网·算法·物流系统·快递系统·跨境物流快递系统·物联网终端实战
bro_Java6669 小时前
《排序的底层逻辑:比较、交换与分治》
java·算法·排序算法