Frog Jump
HARDDescription
A frog is crossing a river. The river is divided into some number of units, and at each unit, there may or may not exist a stone. The frog can jump on a stone, but it must not jump into the water.
Given a list of stones positions (in units) in sorted ascending order, determine if the frog can cross the river by landing on the last stone. Initially, the frog is on the first stone and assumes the first jump must be 1 unit.
If the frog's last jump was k units, its next jump must be either k - 1, k, or k + 1 units. The frog can only jump in the forward direction.
Example 1:
Input: stones = [0,1,3,5,6,8,12,17] Output: true Explanation: The frog can jump to the last stone by jumping 1 unit to the 2nd stone, then 2 units to the 3rd stone, then 2 units to the 4th stone, then 3 units to the 6th stone, 4 units to the 7th stone, and 5 units to the 8th stone.
Example 2:
Input: stones = [0,1,2,3,4,8,9,11] Output: false Explanation: There is no way to jump to the last stone as the gap between the 5th and 6th stone is too large.
Constraints:
2 <= stones.length <= 20000 <= stones[i] <= 231 - 1stones[0] == 0stonesis sorted in a strictly increasing order.
Approaches
Checkout 3 different approaches to solve Frog Jump. Click on different approaches to view the approach and algorithm in detail.
Iterative Dynamic Programming with Hashing
This approach uses a bottom-up dynamic programming strategy, which is often implemented iteratively. It can be thought of as a Breadth-First Search (BFS) on the state space. We build up the solution by finding all possible jump sizes that can reach each stone, starting from the first stone.
Algorithm
- Create a
HashMap<Integer, Set<Integer>>to store, for each stone position, the set of jump sizes that can be made from it. - Initialize the map with all stone positions and empty sets.
- Add a jump of size 1 to the set for the first stone (at position 0):
map.get(0).add(1). - Iterate through the
stonesarray fromi = 0ton-1. - For the current stone
stones[i], get its set of possible jumps from the map. - For each jump
kin the set:- Calculate
nextPos = stones[i] + k. - If
nextPosis the position of the last stone, returntrue. - If there is a stone at
nextPos(check using the map), update the set fornextPosby addingk-1(if > 0),k, andk+1.
- Calculate
- If the loops complete without reaching the last stone, return
false.
We use a HashMap where the keys are the stone positions and the values are Sets of integers. map.get(stone_pos) will store the set of jump sizes that can be made from the stone at stone_pos.
First, we initialize the map for all stone positions with empty sets. The frog starts at stones[0] (position 0) and must make a jump of size 1. So, we initialize map.get(0).add(1). We then iterate through the stones in their given order. For each stone, we look at the set of possible jump sizes k from it. For each jump size k, we calculate the next_position = stone + k. If a stone exists at next_position, we update its entry in the map. Since the last jump was k, the next jump from next_position can be k-1, k, or k+1. We add these valid (positive) jump sizes to the set for next_position.
After iterating through all stones up to the second to last, if we have managed to schedule a jump to the last stone, we return true. If the entire process completes and the last stone was never reached, it's impossible.
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class Solution {
public boolean canCross(int[] stones) {
Map<Integer, Set<Integer>> map = new HashMap<>();
for (int stone : stones) {
map.put(stone, new HashSet<>());
}
// First jump is 1 unit from stone 0.
// This requires a stone at position 1.
if (!map.containsKey(1) || stones[1] != 1) {
return false;
}
map.get(0).add(1);
for (int i = 0; i < stones.length; i++) {
int currentStone = stones[i];
Set<Integer> jumps = map.get(currentStone);
for (int k : jumps) {
int nextPos = currentStone + k;
if (nextPos == stones[stones.length - 1]) {
return true;
}
if (map.containsKey(nextPos)) {
Set<Integer> nextJumps = map.get(nextPos);
if (k - 1 > 0) {
nextJumps.add(k - 1);
}
nextJumps.add(k);
nextJumps.add(k + 1);
}
}
}
return false;
}
}
Complexity Analysis
Pros and Cons
- Generally more efficient in practice than recursion due to avoiding call stack overhead.
- It's a systematic, level-by-level (stone-by-stone) exploration of reachable states.
- Can use significant memory, O(N^2), similar to the memoization approach.
Code Solutions
Checking out 3 solutions in different languages for Frog Jump. Click on different languages to view the code.
class Solution { private Boolean [][] f ; private Map < Integer , Integer > pos = new HashMap <>(); private int [] stones ; private int n ; public boolean canCross ( int [] stones ) { n = stones . length ; f = new Boolean [ n ][ n ]; this . stones = stones ; for ( int i = 0 ; i < n ; ++ i ) { pos . put ( stones [ i ], i ); } return dfs ( 0 , 0 ); } private boolean dfs ( int i , int k ) { if ( i == n - 1 ) { return true ; } if ( f [ i ][ k ] != null ) { return f [ i ][ k ]; } for ( int j = k - 1 ; j <= k + 1 ; ++ j ) { if ( j > 0 ) { int h = stones [ i ] + j ; if ( pos . containsKey ( h ) && dfs ( pos . get ( h ), j )) { return f [ i ][ k ] = true ; } } } return f [ i ][ k ] = false ; } }Video Solution
Watch the video walkthrough for Frog Jump
Similar Questions
5 related questions you might find useful
Subscribe to Scale Engineer newsletter
Learn about System Design, Software Engineering, and interview experiences every week.
No spam, unsubscribe at any time.