LeetCode 每日一题笔记 日期:2026.05.24 题目:1340. 跳跃游戏 V

LeetCode 每日一题笔记

0. 前言

  • 日期:2026.05.24
  • 题目:1340. 跳跃游戏 V
  • 难度:困难
  • 标签:数组、动态规划、记忆化搜索、单调栈

1. 题目理解

问题描述

给定一个整数数组 arr 和整数 d,从下标 i 出发,每次可以向左或向右跳 1~d 步。跳跃规则为:只能跳到比当前位置更低的位置,且路径中不能出现比当前位置更高的元素。你可以从任意下标开始,返回最多可以访问的下标数量。

示例

输入:arr = [6,4,14,6,8,13,9,7,10,6,12], d = 2

输出:4

解释:以 arr[10]=12 为起点,路径为 12→10→9→7,共访问 4 个下标。

2. 解题思路

核心观察

  1. i 能跳到的位置,只能是 i 左右两侧 [i-d, i+d] 范围内、且路径中无更高元素的更低位置。
  2. 这是一个典型的"树形依赖"问题:每个位置的最长跳跃路径,依赖于它能跳到的位置的最长路径,因此可以用记忆化搜索(递归+DP)解决。
  3. 为了优化时间复杂度,可先用单调栈预处理每个位置左右最近的更高元素,直接找到能跳的终点,避免遍历中间所有元素。

算法步骤

基础版(记忆化搜索)

  1. 定义 dp[i] 表示从 i 出发能访问的最大下标数。
  2. 递归遍历每个位置 i,分别向左、向右检查 1~d 步的所有位置,遇到更高元素则停止。
  3. dp[i] = max(dp[j]) + 1 转移,其中 ji 能跳到的位置。
  4. dp 数组缓存结果,避免重复计算。

优化版(单调栈+记忆化)

  1. 用单调栈预处理每个位置 i 左右两侧、距离不超过 d 的最近更高元素 left[i]right[i]
  2. i 只能跳到 left[i]right[i] 之间的更低位置,因此递归时直接跳转,无需遍历中间元素。
  3. memo[i] 缓存结果,时间复杂度从 O(n·d) 优化到 O(n)。

3. 代码实现

java 复制代码
package lc1340;

import java.util.Arrays;


public class Solution {
    int jump(int[] arr, int[] dp, int i, int d) {
        if (dp[i] != -1) return dp[i];
        int res = 1;
        int right = Math.min(arr.length - 1, i + d);
        int left = Math.max(0, i - d);
        // 向左跳
        for (int j = i - 1; j >= left; j--) {
            if (arr[j] >= arr[i]) break;
            res = Math.max(res, jump(arr, dp, j, d) + 1);
        }
        // 向右跳
        for (int j = i + 1; j <= right; j++) {
            if (arr[j] >= arr[i]) break;
            res = Math.max(res, jump(arr, dp, j, d) + 1);
        }
        dp[i] = res;
        return res;
    }

    public int maxJumps(int[] arr, int d) {
        int dp[] = new int[arr.length];
        Arrays.fill(dp, -1);
        int res = 1;
        for (int i = 0; i < arr.length; i++) {
            res = Math.max(res, jump(arr, dp, i, d));
        }
        return res;
    }
}

4. 代码优化说明

java 复制代码
class Solution {
    public int maxJumps(int[] arr, int d) {
        int n = arr.length;

        // 计算 arr[i] 左边最近的更高元素 arr[left[i]]
        int[] left = new int[n];
        int[] st = new int[n];
        int top = -1; // 栈顶下标
        for (int i = 0; i < n; i++) {
            int x = arr[i];
            while (top >= 0 && arr[st[top]] <= x) {
                top--; // 出栈
            }
            // 如果左边没有更高的数,或者跳跃距离超过 d,都标记为 -1
            left[i] = top < 0 || i - st[top] > d ? -1 : st[top];
            st[++top] = i; // 入栈
        }

        // 计算 arr[i] 右边最近的更高元素 arr[right[i]]
        int[] right = new int[n];
        top = -1;
        for (int i = n - 1; i >= 0; i--) {
            int x = arr[i];
            while (top >= 0 && arr[st[top]] <= x) {
                top--;
            }
            // 如果右边没有更高的数,或者跳跃距离超过 d,都标记为 -1
            right[i] = top < 0 || st[top] - i > d ? -1 : st[top];
            st[++top] = i;
        }

        int[] memo = new int[n];

        // 枚举终点,倒着跳
        int ans = 0;
        for (int i = 0; i < n; i++) {
            ans = Math.max(ans, dfs(i, left, right, memo));
        }
        return ans;
    }

    private int dfs(int i, int[] left, int[] right, int[] memo) {
        if (memo[i] == 0) { // 没有计算过
            // 往左跳 vs 往右跳
            int l = left[i] == -1 ? 0 : dfs(left[i], left, right, memo);
            int r = right[i] == -1 ? 0 : dfs(right[i], left, right, memo);
            memo[i] = Math.max(l, r) + 1;
        }
        return memo[i];
    }
}

优化说明

  1. 用单调栈预处理 leftright 数组,避免递归中遍历 1~d 步的所有元素,直接找到能跳的终点。
  2. 简化 dfs 中的 if 分支判断,用三元运算符处理 left[i]right[i]-1 的情况。
  3. 时间复杂度从 O(n·d) 优化到 O(n),空间复杂度为 O(n),避免了大规模用例的超时问题。

5. 复杂度分析

  • 基础版
    • 时间复杂度:O(n·d),每个位置最多遍历 d 步,且每个位置仅计算一次。
    • 空间复杂度:O(n),递归栈深度和 dp 数组均为 O(n)。
  • 优化版
    • 时间复杂度:O(n),单调栈预处理和记忆化搜索均为线性时间。
    • 空间复杂度:O(n),leftrightmemo 数组和单调栈均为 O(n)。

6. 总结

  • 本题核心是利用记忆化搜索 解决树形依赖问题,基础版实现简单但易超时,优化版通过单调栈预处理大幅降低时间复杂度。
  • 解题关键在于理解"路径中无更高元素"的规则,通过预处理找到能跳的终点,避免不必要的遍历。
  • 对于大规模用例,优先使用优化版;基础版适合快速实现和理解问题。
相关推荐
2501_931803757 小时前
Go 接口学习笔记:从语法到心法
笔记·学习·golang
w2018007 小时前
四线三格英语本模板word版pdf版作文纸可打印
笔记
z200509307 小时前
今日算法(组合问题III)(回溯的使用)
java·算法·leetcode
searchforAI7 小时前
5款AI笔记工具实测:导入体验、结构化输出、后续能力逐项对比
人工智能·笔记·学习·ai·chatgpt·aigc·音视频
XGeFei7 小时前
【Fastapi学习笔记(1)】—— Pydantic模型、依赖注入、请求头-Cookie、响应头
笔记·学习·fastapi
早日退休!!!7 小时前
《烧掉数学书:重新发明数学》笔记提炼
笔记
_Evan_Yao7 小时前
游戏和编程两不误:用Unity做一个简单小游戏
后端·游戏·unity·游戏引擎
智者知已应修善业7 小时前
【51单片机第5和6位数码管显示0-99自动计数】2023-11-29
c++·经验分享·笔记·算法·51单片机
Kay_Liang8 小时前
VirtualBox NAT 网络实现三台虚拟机互联踩坑实录
网络·windows·笔记·ubuntu·网络安全