每日一道leetcode(2026.05.07):跳跃游戏 IX

0 阅读3分钟

1. 题目

给你一个整数数组 nums。

从任意下标 i 出发,你可以根据以下规则跳跃到另一个下标 j:

仅当 nums[j] < nums[i]时,才允许跳跃到下标 j,其中 j > i。 仅当 nums[j] > nums[i]时,才允许跳跃到下标 j,其中 j < i。 对于每个下标 i,找出从 i 出发且可以跳跃 任意 次,能够到达 nums 中的 最大值 是多少。

返回一个数组 ans,其中 ans[i] 是从下标 i 出发可以到达的最大值。

示例 1:

输入: nums = [2,1,3]

输出: [2,2,3]

解释:

对于 i = 0:没有跳跃方案可以获得更大的值。 对于 i = 1:跳到 j = 0,因为 nums[j] = 2 大于 nums[i]。 对于 i = 2:由于 nums[2] = 3 是 nums 中的最大值,没有跳跃方案可以获得更大的值。 因此,ans = [2, 2, 3]。

示例 2:

输入: nums = [2,3,1]

输出: [3,3,3]

解释:

对于 i = 0:向后跳到 j = 2,因为 nums[j] = 1 小于 nums[i] = 2,然后从 i = 2 跳到 j = 1,因为 nums[j] = 3 大于 nums[2]。 对于 i = 1:由于 nums[1] = 3 是 nums 中的最大值,没有跳跃方案可以获得更大的值。 对于 i = 2:跳到 j = 1,因为 nums[j] = 3 大于 nums[2] = 1。 因此,ans = [3, 3, 3]。

提示:

1 <= nums.length <= 10e5 1 <= nums[i] <= 10e9

2. 分析

这道题还是挺有意思的,可以从当前位置跳到比当前值大的左侧位置,也可以跳到比当前值小的右侧位置,且可以多次跳跃。值得注意的是,有些值,就是需要经过多次左右跳跃之后才能够获取到目标最大值的。

我的思路是分别记录当前位置左侧的最大值和右侧的最小值,每次移动都先移动到左侧的最大值处,再移动到右侧最远处的比最大值小的位置,以此循环,直到连续两次移动后还在原位置,则证明到达了可及的最右侧。

按照这个思路实现后,加上了数组记忆功能,但还是没有通过最后几道用例,超出时间限制。主要是我在找跳转到最远处下标时,使用了倒序的循环。

最后加了一层预处理,将每个位置跳转的路线通过批处理给初始化,减少了在遍历中的循环,然后就通过啦。

3. 代码实现

import java.util.Arrays;

class Solution {
    public int[] maxValue(int[] nums) {
        int len = nums.length;
        // 最左侧到当前位置出现的最大值
        int[] leftMax = new int[len];
        int maxVal = nums[0];
        for (int i = 0; i < len; i++) {
            leftMax[i] = maxVal = Math.max(maxVal, nums[i]);
        }
        // 最右侧到当前位置出现的最小值
        int[] rightMin = new int[len];
        int minVal = nums[len - 1];
        for (int i = len - 1; i >= 0; i--) {
            rightMin[i] = minVal = Math.min(minVal, nums[i]);
        }
        // 预处理所有的最佳跳跃方案
        int[] jump = new int[len];
        int bestJ = len - 1; // 从后往前,一次遍历搞定
        for (int i = len - 1; i >= 0; i--) {
            int target = leftMax[i];
            while (bestJ > i && rightMin[bestJ] >= target) {
                bestJ--;
            }
            jump[i] = Math.max(bestJ, i);
        }
        // 递归求解
        int[] res = new int[len];
        int[] resMemory = new int[len];
        Arrays.fill(resMemory, 0);
        for (int i = 0; i < len; i++) {
            res[i] = findMaxValue(leftMax, jump, i, resMemory);
        }
        return res;
    }

    public static int findMaxValue(int[] leftMax, int[] jump, int lastFindIndex, int[] resMemory) {
        if (resMemory[lastFindIndex] != 0) {
            // 缓存中已经有了
            return resMemory[lastFindIndex];
        }
        int nextIdx = jump[lastFindIndex];
        if (nextIdx == lastFindIndex) {
            return resMemory[lastFindIndex] = leftMax[lastFindIndex];
        }

        int ans = findMaxValue(leftMax, jump, nextIdx, resMemory);
        resMemory[lastFindIndex] = ans;
        return ans;
    }
}

在这里插入图片描述