Skip to content

Commit 5493cec

Browse files
committed
feat(data structures, graphs): frog position after t seconds
1 parent 68b59fa commit 5493cec

17 files changed

+248
-6
lines changed

algorithms/frog_jumps/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# Frog Jumps
2+
13
A small frog wants to get to the other side of the road. The frog is currently located at position X and wants to get to
24
a position greater than or equal to Y. The small frog always jumps a fixed distance, D.
35

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Frog Position After T Seconds
2+
3+
You are given an undirected tree with n vertices labeled from 1 to `n`. A frog starts at vertex 1, at time 0, and makes
4+
one move per second.
5+
6+
At each step, the frog follows these rules:
7+
8+
1. Move to an unvisited neighbor:
9+
If the frog has unvisited neighbors, it jumps to one of them, chosen uniformly at random (equal probability for each
10+
choice).
11+
12+
2. No revisiting: The frog can not jump back to a vertex it has already visited.
13+
3. Stay when stuck: The frog will keep jumping at its current vertex if there are no unvisited neighbors.
14+
15+
The tree is represented as an array of edges, where `edges[i] = [ai, bi]` means an edge connecting the vertices
16+
`ai` and `bi`. Your task is to return the probability that, after t seconds, the frog is on the vertex target.
17+
18+
Constraints
19+
20+
- 1 ≤ n ≤ 100
21+
- `edges.length` == `n − 1`
22+
- edges[i].length == 2
23+
- 1 ≤ ai, bi ≤ n
24+
- 1 ≤ t ≤ 50
25+
- 1 ≤ target ≤ n
26+
27+
## Examples
28+
29+
![Example 1](./images/examples/front_position_after_t_seconds_example_1.png)
30+
![Example 2](./images/examples/front_position_after_t_seconds_example_2.png)
31+
![Example 3](./images/examples/front_position_after_t_seconds_example_3.png)
32+
![Example 4](./images/examples/front_position_after_t_seconds_example_4.png)
33+
![Example 5](./images/examples/front_position_after_t_seconds_example_5.png)
34+
35+
## Solution
36+
37+
The essence of the solution lies in simulating the frog’s movement with a level-order BFS, where each level is one
38+
second. At any second, if the frog is on a node with unvisited neighbors, it must jump to one of them at random, all
39+
with the same chance, because it cannot revisit nodes, only unvisited neighbors are considered. If a node has no
40+
unvisited neighbors, the frog stays there for the remaining time.
41+
42+
There are three cases for the `target`:
43+
44+
- The frog arrives exactly at second `t` → that chance is the answer.
45+
- The frog arrives earlier, and the `target` has no unvisited neighbors → it stays, so that chance still counts at time `t`.
46+
- The frog arrives earlier, but the `target` does have unvisited neighbors and time remains → it must leave, so that
47+
chance at time t is 0.
48+
49+
Using the intuition above, we implement the algorithm as follows:
50+
51+
1. Build an adjacency list `tree` from `edges` to quickly look up all neighbors of any node.
52+
2. Initialize a queue with a single state (node = 1, probability = 1.0), a visited array of size n + 1 with visited[1] =
53+
true, and set time_left = t.
54+
3. While the queue is not empty and time_left >= 0:
55+
- Record the current level_size as this represents all the frog’s positions at this second.
56+
- Repeat level_size times:
57+
- Pop (u, p) from the queue.
58+
- Count `cnt = number` of unvisited neighbors of u (check visited[v] == false for each neighbor v).
59+
- If u == target:
60+
- If time_left == 0, return p because the frog arrived exactly on time.
61+
- If cnt == 0, return p because the frog arrived earlier and can not move, so it remains on the target.
62+
- Otherwise if time_left > 0 and cnt > 0, then return 0.0 because the frog must leave before the time runs out.
63+
- If cnt > 0:
64+
- For each unvisited neighbor v of u:
65+
- Mark visited[v] = true.
66+
- Enqueue (v, p / cnt), since the probability is split equally among all unvisited neighbors.
67+
- Decrement time_left by 1.
68+
4. If the loop ends without returning (the frog never satisfied the target conditions in time), return 0.0.
69+
70+
Let’s look at the following illustration to get a better understanding of the solution:
71+
72+
![Solution 1](./images/solutions/front_position_after_t_seconds_solution_1.png)
73+
![Solution 2](./images/solutions/front_position_after_t_seconds_solution_2.png)
74+
![Solution 3](./images/solutions/front_position_after_t_seconds_solution_3.png)
75+
![Solution 4](./images/solutions/front_position_after_t_seconds_solution_4.png)
76+
![Solution 5](./images/solutions/front_position_after_t_seconds_solution_5.png)
77+
![Solution 6](./images/solutions/front_position_after_t_seconds_solution_6.png)
78+
79+
### Time complexity
80+
81+
The time complexity of this solution is `O(n)`. Building the adjacency list takes linear time in a tree (E = n − 1).
82+
The BFS then visits each node at most once and inspects each edge at most twice. Even if t is larger than the tree’s
83+
depth, the traversal stops once the queue is empty, so the work does not grow with t.
84+
85+
### Space complexity
86+
87+
The space complexity is `O(n)`. The adjacency list requires O(n) space, the visited array is O(n), and the BFS queue
88+
can hold up to O(n) nodes in the worst case.
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
from typing import List
2+
from collections import defaultdict, deque
3+
4+
5+
def frog_position(n: int, edges: List[List[int]], t: int, target: int) -> float:
6+
# Handle the trivial case of a single vertex
7+
if n == 1:
8+
return 1.0
9+
10+
# Build adjacency list representation of the tree
11+
graph = defaultdict(list)
12+
for a, b in edges:
13+
graph[a].append(b)
14+
graph[b].append(a)
15+
16+
# BFS to track probabilities and arrival times
17+
# Each state: (vertex, parent, probability, time)
18+
queue = deque([(1, -1, 1.0, 0)])
19+
visited = {1}
20+
21+
while queue:
22+
vertex, parent, prob, time = queue.popleft()
23+
24+
# Find unvisited neighbors (excluding the parent we came from)
25+
unvisited_neighbors = [
26+
neighbor for neighbor in graph[vertex] if neighbor not in visited
27+
]
28+
29+
# Check if we're at the target vertex
30+
if vertex == target:
31+
# If we arrive exactly at time t, we're here
32+
if time == t:
33+
return prob
34+
# If we arrive before time t, we can only be here if this is a leaf
35+
# (no unvisited neighbors to continue to)
36+
elif time < t and len(unvisited_neighbors) == 0:
37+
return prob
38+
# Otherwise, we either haven't arrived yet or we moved past it
39+
else:
40+
return 0.0
41+
42+
# If we've used up our time, stop exploring this path
43+
if time >= t:
44+
continue
45+
46+
# Move to each unvisited neighbor with equal probability
47+
if unvisited_neighbors:
48+
# Probability splits equally among all unvisited neighbors
49+
next_prob = prob / len(unvisited_neighbors)
50+
51+
for neighbor in unvisited_neighbors:
52+
visited.add(neighbor)
53+
queue.append((neighbor, vertex, next_prob, time + 1))
54+
55+
# If we never reached the target, probability is 0
56+
return 0.0
57+
58+
59+
def frog_position_2(n: int, edges: List[List[int]], t: int, target: int) -> float:
60+
graph = defaultdict(list)
61+
for u, v in edges:
62+
graph[u].append(v)
63+
graph[v].append(u)
64+
65+
q = deque([(1, 1.0)])
66+
visited = [False] * (n + 1)
67+
visited[1] = True
68+
69+
time_left = t
70+
while q and time_left >= 0:
71+
level_size = len(q)
72+
73+
for _ in range(level_size):
74+
u, p = q.popleft()
75+
76+
cnt_unvisited = 0
77+
for v in graph[u]:
78+
if not visited[v]:
79+
cnt_unvisited += 1
80+
81+
if u == target:
82+
if time_left == 0 or cnt_unvisited == 0:
83+
return p
84+
return 0.0
85+
86+
if cnt_unvisited > 0:
87+
split = p / cnt_unvisited
88+
for v in graph[u]:
89+
if not visited[v]:
90+
visited[v] = True
91+
q.append((v, split))
92+
time_left -= 1
93+
return 0.0
118 KB
Loading
148 KB
Loading
134 KB
Loading
125 KB
Loading
162 KB
Loading
107 KB
Loading
157 KB
Loading

0 commit comments

Comments
 (0)