The O(n) Club: Frog Jump — Can Your Recursion Swim, or Will It Croak?
⚡ TL;DR
Frog at stone 0 wants to land on the last stone, only jumping forward by k-1, k, or k+1 units (where k is his last jump). The first jump is always a modest 1. Brute force will drown your CPU; memoization is how your frog stays dry. Here’s a Java sketch to get the river water swirling:
// Brute force (don't blame me if it times out) boolean canCross(int[] stones) { return jump(stones, 0, 0); } boolean jump(int[] stones, int idx, int k) { if (idx == stones.length - 1) return true; for (int next = idx + 1; next < stones.length; next++) { int gap = stones[next] - stones[idx]; if (gap >= k - 1 && gap <= k + 1 && gap > 0) { if (jump(stones, next, gap)) return true; } } return false; }
🧠 How Devs Usually Mess This Up
Common land mines:
- Ignoring that the very first jump must be exactly 1. No short cuts, no creative liberties.
- Letting the frog hop backwards (bonus points if you want a time machine, but not here).
- Blind recursion, no memoization—see also: “interviewer sighs, your stack overflows, and the frog silently drifts downstream.”
- Assuming jumps must change every time, when repeats are allowed if a stone obediently sits at the target spot.
🔍 What’s Actually Going On
This is Frogger with dignity: after each heroic leap (of k units), the next jump must bravely differ by at most 1 (k-1, k, or k+1 units). The frog never backtracks and only lands where a stone exists. Thus, you always track two features in parallel universe:
- Where’s the frog? (The location/stone position)
- How big was the last jump? (Jump size)
Repeat that state, repeat your doom—so cache it! When picking a jump, always check if there’s a stone at the landing spot, or this frog’s going for an unwanted swim. Good thing Sets turn river navigation into a lookup party.
🛠️ PseudoCode
- Build a HashSet of all stone positions.
- Write a recursion:
canCross(position, jumpSize). - Base case: If
position == last stone, congrats—frog is home. - For
possibleJumpinjumpSize-1,jumpSize,jumpSize+1:- Skip if possibleJump <= 0. (Only positive hops allowed—no reverse or hover mode!)
- Calculate
nextPosition = position + possibleJump - If nextPosition is a stone:
canCross(nextPosition, possibleJump)
- Memoize (position, jumpSize) pairs where the frog fails, or it’ll keep trying the same damp magic trick.
- Remember: first jump must be exactly 1.
// Start: canCross(stones[0], 0) // First move: try only jump size 1
💻 The Code
import java.util.*;
public class FrogJump {
public boolean canCross(int[] stones) {
Set
<integer> stoneSet = new HashSet<>();
for (int s : stones) stoneSet.add(s);
Map<string boolean> memo = new HashMap<>();
return dfs(stones[0], 0, stones[stones.length - 1], stoneSet, memo);
}
private boolean dfs(int pos, int jump, int last, Set
<integer> stoneSet, Map<string boolean> memo) {
String key = pos + "," + jump;
if (memo.containsKey(key)) return memo.get(key);
if (pos == last) return true;
for (int step = jump - 1; step <= jump + 1; step++) {
if (step <= 0) continue;
int next = pos + step;
if (stoneSet.contains(next)) {
if (dfs(next, step, last, stoneSet, memo)) {
memo.put(key, true);
return true;
}
}
}
memo.put(key, false);
return false;
}
}</string></integer></string></integer>
⚠️ Pitfalls, Traps & Runtime Smacks
- Stubbing your first jump: No stone at distance 1? Don’t start packing for Paris—the frog’s going nowhere.
- Negative or zero jumps: Only valid hops are >0. The rest is frog yoga, not coding.
- No memoization, exponential misery: Miss the memo, explode the runtime (and maybe your soul).
- Time/space: There are at most O(N^2) (position, jumpSize) combos. Manageable—but don’t feed the input steroids.
🧠 Interviewer Brain-Tattoo
“If you don’t memoize, this frog and your interview performance both drown. Track your state—or get ready for wet socks.”