给你一个整数数组 arr ,你一开始在数组的第一个元素处(下标为 0)。
每一步,你可以从下标 i 跳到下标:
- i + 1 满足:i + 1 < arr.length
- i - 1 满足:i - 1 >= 0
- j 满足:arr[i] == arr[j] 且 i != j
请你返回到达数组最后一个元素的下标处所需的 最少操作次数 。
注意:任何时候你都不能跳到数组外面。
示例 1:
输入:arr = [100,-23,-23,404,100,23,23,23,3,404]
输出:3
解释:那你需要跳跃 3 次,下标依次为 0 --> 4 --> 3 --> 9 。下标 9 为数组的最后一个元素的下标。
示例 2:
输入:arr = [7]
输出:0
解释:一开始就在最后一个元素处,所以你不需要跳跃。
示例 3:
输入:arr = [7,6,9,6,9,6,9,7]
输出:1
解释:你可以直接从下标 0 处跳到下标 7 处,也就是数组的最后一个元素处。
示例 4:
输入:arr = [6,1,9]
输出:2
示例 5:
输入:arr = [11,22,7,7,7,7,7,7,7,22,13]
输出:3
提示:
- 1 <= arr.length <= 5 * 10^4
- -10^8 <= arr[i] <= 10^8
分析:
方法1:BFS + 哈希表
该游戏在跳跃的方式中有向左右跳和同数跳的方式,由于求跳的最小次数,所以很容易让人想到广度优先搜索(BFS),定义一个队列代表一层,一层遍历完后,将该层入栈,再将下一层的索引入栈,每一层代表跳的次数,如果跳到底,就返回该层数。
这时候为了避免多次跳跃,我们可以定义一个 HashSet 用来储存跳过的位置,如果重复就不跳。
关于同数跳跃,为避免多次操作,可以定义 HashMap 来存储每个数的索引,因为索引可以不止一个,值可以用 List 来存储,这个数的索引入栈后就将该数删除,避免造成内存浪费。
时间复杂度:O(n+e)
空间复杂度:O(n)
class Solution {
public int minJumps(int[] arr) {
//数组
int len = arr.length;
//存储对应数字索引
Map<Integer, List<Integer>> map = new HashMap<>();
for(int i = len-1; i >= 0; i--){
List<Integer> list = map.getOrDefault(arr[i], new ArrayList<>());
list.add(i);
map.put(arr[i], list);
}
//定义队列以便进行广度遍历
Deque<Integer> queue = new ArrayDeque<>();
//定义最小值
int min = 0;
//将首索引入队列
queue.add(0);
//记录重复索引
HashSet<Integer> set = new HashSet<>();
//到头停止循环
while(true){
int size = queue.size();
while(size > 0){
size--;
int i = queue.pollFirst();
//去重,防止重复操作
if(set.contains(i)){
continue;
}
set.add(i);
//到头了,终止循环
if(i == len - 1){
return min;
}
//入栈左边
if(i - 1 > 0){
queue.offerLast(i - 1);
}
//入栈右边
if(i + 1 < len){
queue.offerLast(i + 1);
}
//入栈相容值的索引
List<Integer> list = map.getOrDefault(arr[i], new ArrayList<>());
for(int l: list){
queue.offerLast(l);
}
map.remove(arr[i]);
}
min++;
}
}
}
方法2:BFS+数组
由 方法1 的代码可以看出,在每一层都要用一次 while 循环来进行出栈入栈操作,在空间和时间上会造成一定的浪费,那么有没有一种方法可以不用 while 循环也可以达到相应操作的目的呢?想想之所以用 while ,是因为保证那一层的数组都出栈完,好记录跳跃的次数,既然如此,我们是不是就可以将 HashSet 替换成数组,然后数组的索引代表遍历对象数组的索引,利用数组来存储索引对应跳跃的次数,每一次遍历只要将数组的值取出来就可以了。
时间复杂度:O(n)
空间复杂度:O(n)
class Solution {
public int minJumps(int[] arr) {
//数组
int len = arr.length;
//存储对应数字索引
Map<Integer, List<Integer>> map = new HashMap<>();
for(int i = len-1; i >= 0; i--){
List<Integer> list = map.getOrDefault(arr[i], new ArrayList<>());
list.add(i);
map.put(arr[i], list);
}
//定义队列以便进行广度遍历
Deque<Integer> queue = new ArrayDeque<>();
//将首索引入队列
queue.add(0);
//记录每个索引遍历的层数
int[] steps = new int[len];
//到头停止循环
while(true){
int i = queue.pollFirst(), step = steps[i];
//到头了,终止循环
if(i == len - 1){
return step;
}
step++;
//入栈左边
if(i - 1 > 0 && steps[i - 1] == 0){
steps[i - 1] = step;
queue.offerLast(i - 1);
}
//入栈右边
if(i + 1 < len && steps[i + 1] == 0){
steps[i + 1] = step;
queue.offerLast(i + 1);
}
//入栈相容值的索引
List<Integer> list = map.getOrDefault(arr[i], new ArrayList<>());
for(int l: list){
if(steps[l] == 0){
steps[l] = step;
queue.offerLast(l);
}
}
map.remove(arr[i]);
}
}
}
题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game-iv