leetcode
leetcode 1701 ~ 1750
跳跃游戏 VIII

跳跃游戏 VIII

难度:

标签:

题目描述

代码结果

运行时间: 218 ms, 内存: 32.2 MB


/*
题目:跳跃游戏 VIII
题目编号:2297

题目描述:给定一个数组,表示每个位置可以跳跃的最大步数,判断是否可以从第一个位置跳到最后一个位置。

思路:
1. 使用IntStream来遍历数组。
2. 用AtomicInteger来记录最远可达位置。
3. 在遍历过程中,更新最远可达位置。
4. 如果最远可达位置大于等于最后一个位置,则返回true。
*/

import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

public class JumpGameVIIIStream {
    public boolean canJump(int[] nums) {
        AtomicInteger maxReach = new AtomicInteger(0);
        return IntStream.range(0, nums.length).allMatch(i -> {
            if (i > maxReach.get()) {
                return false;
            }
            maxReach.set(Math.max(maxReach.get(), i + nums[i]));
            return maxReach.get() >= nums.length - 1 || i < nums.length - 1;
        });
    }

    public static void main(String[] args) {
        JumpGameVIIIStream solution = new JumpGameVIIIStream();
        int[] nums = {2, 3, 1, 1, 4};
        System.out.println(solution.canJump(nums)); // 输出 true
    }
}

解释

方法:

题解使用了动态规划和单调栈的结合来解决问题。动态规划数组 `dp[i]` 代表从起点到达点 `i` 的最小成本。解决思路是遍历每一个位置 `i`,并维护两个单调栈:`maxStk` 和 `minStk`。`maxStk` 维护一个单调递减的栈,其中 `nums` 的值是递减的;当遇到一个比栈顶更大的元素时,我们从栈顶移除元素,并更新 `dp[i]`。类似地,`minStk` 维护一个单调递增的栈,用于处理当前元素比栈顶元素小的情况。通过这种方式,我们能够在遍历数组的同时,快速找到之前的位置 `k`,使得从 `k` 到 `i` 的转移成本最小,即 `dp[i] = min(dp[i], dp[k] + cost[i])`。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在解题中,为何选择使用单调栈结合动态规划而不是仅使用动态规划或其他数据结构?
单独使用动态规划来解决此类问题时,我们通常需要对每个位置i考虑所有可能的前驱位置k,这样的时间复杂度会达到O(n^2),对于大数据量的情况下计算量过大。单调栈的使用可以帮助我们在O(n)的时间复杂度内通过维护一个有序的结构,快速找到满足条件的前驱位置k,从而有效地优化状态转移方程的求解过程。此外,单调栈可以帮助我们在遍历数组的同时,维护当前元素左侧的一个有序序列,这种方法比使用其他数据结构如堆或二叉搜索树在此问题中更为直接和高效。
🦆
单调栈在这种情况下如何具体帮助减少计算量,能否详细解释它是如何优化状态转移方程的计算的?
单调栈通过维护一个单调的元素序列,使得每次计算dp[i]时,我们只需要考虑栈中的有限几个元素而不是全部历史元素。例如,单调递减栈确保了栈中任何一个位置j的nums[j]都大于栈顶位置k的nums[k],这意味着对于任何非栈顶元素,一旦新的元素比栈顶大或相等,栈顶元素就不再有可能是最优选择,可被移除。这种机制减少了不必要的比较和计算,使得每个元素平均只被处理一次,从而将时间复杂度从O(n^2)降低到O(n)。
🦆
题解中提到单调递增栈和单调递减栈同时维护,这种做法在解决问题中起到了什么作用,它们各自又是如何影响dp数组的更新的?
同时维护单调递增栈和单调递减栈可以让我们根据不同的条件快速找到最优的前驱位置。单调递减栈用于找到所有nums值大于当前元素的最近位置,这有助于我们在计算成本时采用更大的nums值作为基准。相反,单调递增栈帮助我们找到nums值小于当前元素的最近位置,这在某些情况下可能提供更小的转移成本。每个栈的维护保证了在每个位置i的计算中,我们总是能够快速找到最优的前驱k,从而更新dp[i] = min(dp[i], cost + dp[k]),确保了算法的高效性和正确性。

相关问题