leetcode
leetcode 1251 ~ 1300
跳跃游戏 IV

跳跃游戏 IV

难度:

标签:

题目描述

代码结果

运行时间: 122 ms, 内存: 33.1 MB


/*
 * 思路:同样是使用广度优先搜索(BFS),但这次利用Java Stream API进行部分简化。
 * 首先用流来构建值到索引的映射。
 * 然后使用一个队列进行BFS搜索,记录访问过的节点并计算步数。
 */

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Solution {
    public int minJumps(int[] arr) {
        if (arr.length <= 1) return 0;

        Map<Integer, List<Integer>> valueToIndices = IntStream.range(0, arr.length)
            .boxed()
            .collect(Collectors.groupingBy(i -> arr[i]));

        Queue<Integer> queue = new LinkedList<>();
        queue.offer(0);
        boolean[] visited = new boolean[arr.length];
        visited[0] = true;
        int steps = 0;

        while (!queue.isEmpty()) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                int currentIndex = queue.poll();
                if (currentIndex == arr.length - 1) return steps;

                List<Integer> nextIndices = new ArrayList<>(valueToIndices.getOrDefault(arr[currentIndex], new ArrayList<>()));
                if (currentIndex + 1 < arr.length) nextIndices.add(currentIndex + 1);
                if (currentIndex - 1 >= 0) nextIndices.add(currentIndex - 1);

                for (int nextIndex : nextIndices) {
                    if (nextIndex >= 0 && nextIndex < arr.length && !visited[nextIndex]) {
                        queue.offer(nextIndex);
                        visited[nextIndex] = true;
                    }
                }
                nextIndices.clear();
            }
            steps++;
        }
        return -1;
    }
}

解释

方法:

这个问题可以通过使用宽度优先搜索(BFS)来解决。首先,我们使用一个字典 `d` 来存储数组中每个元素的所有下标。接着,我们从数组的第一个元素开始,使用一个队列 `cur` 来保存当前可以访问的下标。每次从队列中取出一个下标,我们都会尝试向左跳(i-1)、向右跳(i+1),以及跳到所有值相同的位置(通过查找字典 `d`)。使用一个数组 `dp` 来记录到达每个下标的最少操作次数,初始时,除了第一个元素设为0以外,其余都设为-1表示未访问。此外,我们使用一个集合 `visited_num` 来记录已经使用过的数字,避免重复访问导致的无效操作。算法的核心在于逐层扩展,直到到达数组的最后一个元素。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在BFS中使用数组dp来记录每个下标的最少操作次数,而不是直接使用队列中的层数来代表步数?
在BFS中使用dp数组的主要原因是,它可以帮助在算法执行过程中多次访问同一个下标时,确保我们只记录该下标的最小步数。尽管队列的层数可以表示从起始点到当前层的步数,但是在题目中可能存在多种方式达到同一个位置,步数可能不同。使用dp数组可以存储到达每个位置的最短路径,避免重复和不必要的计算,同时也便于更新和查询每个位置的最短步数。
🦆
在题解中,visited_num集合是用来记录已经访问过的数字,这样做的目的是什么?是否存在某些情况下重复访问相同的数字值是必要的?
visited_num集合用来记录已经访问过的数字的目的是为了减少不必要的重复访问和计算,特别是当数组中存在多个相同的元素时。例如,如果一个数字在数组中出现多次,那么在第一次处理这个数字时,我们会探索所有相同值的下标;而后续再次遇到相同的数字时,已经没有必要再次探索,因为这些位置已在第一次被完整考虑过。通常情况下,重复访问相同数字值是不必要的,因为第一次访问已经处理了所有相关的下标,除非这些下标的访问条件在后续操作中发生变化,这在大多数BFS应用场景中是不会发生的。
🦆
题解中提到,当找到目标位置n-1时就可以立即结束搜索。这种提前终止搜索的方法是否可能导致没有探索到其他可能的最短路径?
在宽度优先搜索中,当我们第一次到达目标位置n-1时,我们可以确信找到的是最短路径。这是因为BFS是按层次进行扩展的,确保每次扩展的都是当前步数最少的节点。因此,当我们到达n-1时,根据BFS的性质,那一定是从起点到终点的最短路径。所以,提前终止搜索不会遗漏其他可能的最短路径。
🦆
在处理跳到所有值相同的位置时,为什么选择在访问过数字后再添加到visited_num集合,而不是在取出数字时就添加?
选择在访问过数字后再将其添加到visited_num集合的原因是为了确保在当前层中,所有包含该数字的位置都能被正确处理。如果我们在取出数字时就将其添加到visited_num集合,那么在当前层的后续操作中,如果再次遇到相同的数字,我们将无法访问到该数字的其他下标。这可能会导致错过某些必要的步骤,进而影响到达某些位置的最优解。通过在处理完所有当前层的相同值之后再添加,我们可以确保每个数字在每层中只被处理一次,同时不漏掉任何可能的跳跃。

相关问题