Problem Statement:-
Given an array of integers arr
, you are initially positioned at the first index of the array.
In one step you can jump from index i
to index:
i + 1
where:i + 1 < arr.length
.i - 1
where:i - 1 >= 0
.j
where:arr[i] == arr[j]
andi != j
.
Return the minimum number of steps to reach the last index of the array.
Notice that you can not jump outside of the array at any time.
Link: leetcode.com/problems/jump-game-iv/descript..
Problem Explanation with examples:-
Example 1
Input: arr = [100,-23,-23,404,100,23,23,23,3,404]
Output: 3
Explanation: You need three jumps from index 0 --> 4 --> 3 --> 9. Note that index 9 is the last index of the array. The minimum number of jumps to reach the last index is 3.
Example 2
Input: arr = [7]
Output: 0
Explanation: Start index is the last index. You don't need to jump.
Example 3
Input: arr = [7,6,9,6,9,6,9,7]
Output: 1
Explanation: You can jump directly from index 0 to index 7 which is last index of the array as the first and last index have the same value and we are allowed to jump to any index with the same value. So, we need only 1 jump.
Example 4
Input: arr = [6,1,9]
Output: 2
Explanation: The shortest way is to jump from index 0 to index 1 using 1 jump and then from index 1 to index 2 using 1 jump.
Example 5
Input: arr = [11,22,7,7,7,7,7,7,7,22,13]
Output: 3
Explanation: The shortest way is to jump from index 0 to index 1 (11 --> 22) using 1 jump and then from index 1 to index 9 (22 --> 22) using 1 jump and then from index 9 to index 10 (22 --> 13) using 1 jump.
Intuition:-
The first approaches that might hit will be BFS, DFS, DP. We will focus here on BFS.
How do we think about bfs here? We need to find the shortest path from the first index to the last index. So, we can use BFS to solve this problem as BFS is used to find the shortest path in a graph.
We can think of the array as a graph where each index is a node and the value of the node is the value of the array at that index.
We can connect two nodes if the values of the nodes are equal. So, we can connect the nodes with the same value. We can also connect the nodes with the index difference of 1.
The problem can also be solved using DFS but BFS is more efficient in this case. DP with memoization will also work but it will take more time than BFS.
Solution:-
- We will check if the length of the array is 1. If it is, then we will return 0.
n = len(arr)
if n == 1:
return 0
So using BFS and for that we first have to store the indices of the same value in a dictionary or a hashmap.
First we will create an empty dictionary and then we will iterate over the array and store the indices of the same value in the dictionary.
value_to_index ={}
for i in range(n):
value_to_index.setdefault(arr[i],[]).append(i)
Then we will create a visited array and initialize it with False. We will also create a queue and append the first index to it.
Then we will create a variable steps and initialize it with 0.
visited = [False]* n
visited[0] = True
queue = collections.deque([0])
steps =0
In the while loop, we will first get the size of the queue and then we will run a for loop for the size of the queue.
In the for loop, we will first pop the first element from the queue and then we will check if the popped element is the last index of the array. If it is, then we will return the steps.
while queue:
size =len(queue)
for _ in range(size):
curr = queue.popleft()
if curr == n -1:
return steps
If the popped element is not the last index of the array, then we will check if the popped element-1 is greater than or equal to 0 and if the popped element-1 is not visited. If both the conditions are true, then we will mark the popped element-1 as visited and append it to the queue.
Then we will check if the popped element+1 is less than the length of the array and if the popped element+1 is not visited. If both the conditions are true, then we will mark the popped element+1 as visited and append it to the queue.
if curr -1>=0 and not visited[curr -1]:
visited[curr -1]= True
queue.append(curr -1)
if curr +1< n and not visited[curr +1]:
visited[curr +1]= True
queue.append(curr +1)
Then we will check if the value of the popped element is present in the dictionary or not. If it is present, then we will iterate over the indices of the value in the dictionary and check if the index is not visited. If the index is not visited, then we will mark the index as visited and append it to the queue.
Then we will delete the value from the dictionary so that we don’t visit the same index again.
for index in value_to_index.get(arr[curr], []):
if not visited[index]:
visited[index] = True
queue.append(index)
if value_to_index.get(arr[curr],-1) != -1:
del value_to_index[arr[curr]]
- After the for loop, we will increment the steps by 1.
steps +=1
- After the while loop, we will return the steps.
Code:-
JAVA Solution
import java.util.*;
class Solution {
public int minJumps(int[] arr) {
int n = arr.length;
if (n == 1) {
return 0;
}
Map<Integer, List<Integer>> valueToIndex = new HashMap<>();
for (int i = 0; i < n; i++) {
valueToIndex.computeIfAbsent(arr[i], k -> new ArrayList<>()).add(i);
}
boolean[] visited = new boolean[n];
visited[0] = true;
Queue<Integer> queue = new LinkedList<>();
queue.offer(0);
int steps = 0;
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
int curr = queue.poll();
if (curr == n - 1) {
return steps;
}
if (curr - 1 >= 0 && !visited[curr - 1]) {
visited[curr - 1] = true;
queue.offer(curr - 1);
}
if (curr + 1 < n && !visited[curr + 1]) {
visited[curr + 1] = true;
queue.offer(curr + 1);
}
for (int index : valueToIndex.getOrDefault(arr[curr], new ArrayList<>())) {
if (!visited[index]) {
visited[index] = true;
queue.offer(index);
}
}
if (valueToIndex.containsKey(arr[curr])) {
valueToIndex.remove(arr[curr]);
}
}
steps++;
}
return steps;
}
}
Python Solution
class Solution:
def minJumps(self,arr: List[int])-> int:
n =len(arr)
if n ==1:
return0
value_to_index ={}
for i inrange(n):
value_to_index.setdefault(arr[i],[]).append(i)
visited =[False]* n
visited[0]= True
queue = collections.deque([0])
steps =0
whilequeue:
size =len(queue)
for _ inrange(size):
curr = queue.popleft()
if curr == n -1:
return steps
if curr -1>=0 and not visited[curr -1]:
visited[curr -1]= True
queue.append(curr -1)
if curr +1< n and not visited[curr +1]:
visited[curr +1]= True
queue.append(curr +1)
for index in value_to_index.get(arr[curr],[]):
if not visited[index]:
visited[index]= True
queue.append(index)
if value_to_index.get(arr[curr],-1)!=-1:
del value_to_index[arr[curr]]
steps +=1
return steps
Complexity Analysis:-
TIME:-
Time complexity is O(n), where N is the length of the array. We will iterate over the array once and we will also iterate over the indices of the same value in the dictionary.
SPACE:-
Space complexity is O(N), where N is the length of the array. We will store the indices of the same value in the dictionary and we will also use a visited array and a queue