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 <= 2000
0 <= stones[i] <= 231 - 1
stones[0] == 0
stones
is sorted in a strictly increasing order.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The frog wants to cross a river by hopping on stones. The brute force method is like trying every single possible sequence of jumps the frog could make to see if it works.
Here's how the algorithm would work step-by-step:
def can_cross_river_brute_force(stones):
stones_set = set(stones)
last_stone = stones[-1]
def can_reach_destination(current_stone, current_jump_size):
if current_stone == last_stone:
return True
# Try all possible jump sizes: k-1, k, k+1
for next_jump_size in range(current_jump_size - 1, current_jump_size + 2):
if next_jump_size > 0:
next_stone = current_stone + next_jump_size
# Ensure the next stone exists
if next_stone in stones_set:
# Recursively check if frog can reach the destination
if can_reach_destination(next_stone, next_jump_size):
return True
return False
# The frog always starts at the first stone with a jump size of 1
if stones[0] == 0 and 1 in stones_set:
# Initial jump of 1 from the first stone
return can_reach_destination(1, 1)
else:
return False
The frog can jump to a stone only if it has hopped that exact distance before. The optimal solution tracks possible jump lengths the frog can make from each stone. This way, we can determine if the frog can reach the final stone without trying all combinations.
Here's how the algorithm would work step-by-step:
def can_cross(stones): stone_positions = set(stones)
if stones[1] != 1:
return False
# Keep track of possible jump lengths
jump_lengths = {stone: set() for stone in stones}
jump_lengths[stones[0]].add(0)
for stone in stones:
for jump_length in jump_lengths[stone]:
for next_jump in [jump_length - 1, jump_length, jump_length + 1]:
if next_jump > 0 and (stone + next_jump) in stone_positions:
# The frog can jump to the next stone
jump_lengths[stone + next_jump].add(next_jump)
# Check if the frog can reach the last stone
return bool(jump_lengths[stones[-1]])
Case | How to Handle |
---|---|
Empty stones array | Return true if the array is empty, as the frog starts at position 0, which is considered a success. |
Stones array with only the first stone (index 0) | Return true, as the frog starts at the first stone and has already succeeded. |
Stones array where the second stone is not at position 1 | Return false, because the frog can only jump 1 unit from the initial position. |
Stones are not strictly increasing | Handle this edge case by sorting the stones array and checking for valid jumps in the sorted array. |
Large difference between consecutive stones | Limit the jump size to a reasonable range (e.g., k-1, k, k+1), and if the gap is too large, backtrack or return false. |
Integer overflow when calculating jumps or stone positions | Use long integers for calculations or add overflow checks when calculating jumps to prevent unexpected behavior. |
Maximum number of stones causing stack overflow in recursive implementations | Use dynamic programming (memoization) instead of recursion to avoid stack overflow with large inputs. |
No path exists to the last stone | Return false if the dynamic programming table indicates that the frog cannot reach the last stone from any reachable position. |