1466. Reorder Routes to Make All Paths Lead to The City Zero - Explanation
Description
There are n cities labeled 0 through n - 1. The pairs in connections describe one-way roads: [a, b] lets you travel from city a to city b. Ignoring their directions, the roads form a tree.
You may reverse the direction of any road. Return the minimum number of reversals needed so that every city has a directed path to city 0.
Reversing a road changes only its direction; its endpoints remain the same.
Example 1:
Input: n = 4
connections = [[0,1],[2,1],[2,3]]
Output: 2Explanation: Reverse 0 → 1 and 2 → 3. The resulting roads are 1 → 0, 2 → 1, and 3 → 2, so every city can reach 0. Both reversals are necessary.
Example 2:
Input: n = 4
connections = [[1,0],[2,0],[3,0]]
Output: 0Explanation: Each of cities 1, 2, and 3 already has a road directly to city 0. No road needs to change direction.
Example 3:
Input: n = 5
connections = [[0,1],[0,2],[0,3],[0,4]]
Output: 4Explanation: All four roads point away from city 0. Each leaf has only one road, so all four roads must be reversed.
Constraints:
2 <= n <= 50000connections.length == n - 1connections[i].length == 2- Each road joins two distinct city labels between
0andn - 1. - Ignoring road directions, the connections form a tree.
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Graph Representation - Building adjacency lists from edge lists to represent graph connections
- Depth First Search (DFS) - Recursively traversing all nodes in a graph while tracking visited nodes
- Breadth First Search (BFS) - Level-by-level graph traversal using a queue
- Hash Set - Efficiently storing and checking edge directions in O(1) time
1. Depth First Search - I
Intuition
We need all cities to reach city 0, so we traverse outward from city 0 and check edge directions. Build an undirected neighbors graph for traversal, but store original edges in edges set to check direction. When moving from city A to neighbor B, if the original edge goes from A to B (away from 0), it needs to be reversed. dfs ensures we visit every city exactly once.
Algorithm
- Store all original directed edges in a set called
edgesas(a, b)pairs. - Build an adjacency list called
neighborstreating edges as undirected. - Start
dfsfrom city 0, marking visited cities invisitset. - For each
neighbor:- If not visited, check if
(neighbor, city)exists in theedgesset. - If not, the edge points away from 0 and needs reversal; increment
changes. - Recursively visit the
neighbor.
- If not visited, check if
- Return the total count of edges to reverse.
class Solution:
def minReorder(self, n: int, connections: List[List[int]]) -> int:
edges = {(a, b) for a, b in connections}
neighbors = {city: [] for city in range(n)}
visit = set()
changes = 0
for a, b in connections:
neighbors[a].append(b)
neighbors[b].append(a)
def dfs(city):
nonlocal changes
visit.add(city)
for neighbor in neighbors[city]:
if neighbor in visit:
continue
if (neighbor, city) not in edges:
changes += 1
dfs(neighbor)
dfs(0)
return changesTime & Space Complexity
- Time complexity:
- Space complexity:
2. Depth First Search - II
Intuition
Instead of using a separate set to track edge directions, we can encode direction information directly in the adjacency list. When adding edges, we store the original direction as a positive value and the reverse as negative. During dfs, positive nei values indicate edges pointing away from city 0, which need reversal.
Algorithm
- Build an adjacency list called
adjwhere each edge(u, v)addsvtoadj[u]and-utoadj[v]. - Start
dfsfrom city 0 withparent = -1. - For each
nei(neighbor):- Skip if
abs(nei)equals theparent(to avoid going back). - Add 1 to the count if
nei > 0(edge needs reversal). - Recursively process
abs(nei)as the nextnode.
- Skip if
- Return the total count.
class Solution:
def minReorder(self, n: int, connections: List[List[int]]) -> int:
adj = [[] for _ in range(n)]
for u, v in connections:
adj[u].append(v)
adj[v].append(-u)
def dfs(node, parent):
changes = 0
for nei in adj[node]:
if abs(nei) == parent:
continue
changes += dfs(abs(nei), node) + (nei > 0)
return changes
return dfs(0, -1)Time & Space Complexity
- Time complexity:
- Space complexity:
3. Breadth First Search
Intuition
bfs works equally well since we just need to visit all nodes once from city 0. We store each edge with a flag isForward indicating if it is a forward edge (pointing away from city 0). Processing level by level in queue, whenever we traverse a forward edge, we count it as needing reversal.
Algorithm
- Build an adjacency list called
adjwhere each edge(u, v)stores(v, 1)inadj[u]and(u, 0)inadj[v]. - Initialize a
queuewith city 0 and mark it visited invisitarray. - While the
queueis not empty:- Dequeue a
node. - For each unvisited
neighbor, mark it visited, addisForwardto the count, and enqueue it.
- Dequeue a
- Return the total count.
class Solution:
def minReorder(self, n: int, connections: List[List[int]]) -> int:
adj = [[] for _ in range(n)]
for u, v in connections:
adj[u].append((v, 1))
adj[v].append((u, 0))
visit = [False] * n
queue = deque([0])
visit[0] = True
changes = 0
while queue:
node = queue.popleft()
for neighbor, isForward in adj[node]:
if not visit[neighbor]:
visit[neighbor] = True
changes += isForward
queue.append(neighbor)
return changesTime & Space Complexity
- Time complexity:
- Space complexity:
Common Pitfalls
Treating the Graph as Directed Only
A common mistake is building only a directed adjacency list based on the given edges. Since we need to traverse the entire tree starting from city 0, we must treat edges as undirected for traversal purposes while separately tracking the original direction to count reversals.
Reversing the Logic of Edge Direction Check
When checking if an edge needs reversal, it is easy to get the condition backwards. Remember that edges pointing away from city 0 need reversal. If traversing from node A to neighbor B and the original edge is (A, B), it points away from 0 and must be counted; if the original edge is (B, A), it already points toward 0.
Not Handling the Sign Encoding for Node 0
In the DFS-II approach that uses negative values to encode direction, node 0 cannot be represented as negative since -0 == 0. This edge case must be handled by using the parent check (abs(nei) == parent) rather than relying solely on sign for all nodes.
Sign in to join the discussion