3383. Minimum Runes to Add to Cast Spell π
Problem Description
Alice needs to cast a magic spell that involves n
focus points (numbered from 0 to n-1). Some focus points contain magic crystals that serve as energy sources, specified in the crystals
array. Focus points are connected by directed runes that allow magic to flow from one point to another, represented by the flowFrom
and flowTo
arrays where the i-th rune creates a directed edge from flowFrom[i]
to flowTo[i]
.
For the spell to work properly, every focus point must satisfy at least one of these conditions:
- It contains a magic crystal (is listed in the
crystals
array) - It receives magic flow from at least one other focus point (has at least one incoming edge)
The problem asks you to find the minimum number of new directed runes (edges) that Alice must add to ensure all focus points meet one of these requirements.
For example, if a focus point doesn't have a crystal and doesn't receive any incoming magic flow, you need to add a rune that directs magic to it from some other point. The goal is to minimize the total number of runes added while ensuring every focus point is either a crystal source or receives magic from somewhere else.
The solution involves identifying which focus points are already "satisfied" (either have crystals or can be reached from crystal points through existing runes) and then determining the minimum number of additional connections needed to satisfy the remaining "unsatisfied" focus points.
Flowchart Walkthrough
First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:
Is it a graph?
- Yes: The problem explicitly involves focus points (nodes) and directed runes (edges) that channel magic flow from one focus point to another. We have
n
focus points and directed edges defined byflowFrom
andflowTo
arrays.
Is it a tree?
- No: The graph can have multiple components, cycles, and nodes with multiple incoming edges. It's a general directed graph, not a tree structure.
Is the problem related to directed acyclic graphs?
- No: While we have a directed graph, the problem doesn't specifically require it to be acyclic. The main concern is about reachability and connectivity from crystal points.
Is the problem related to shortest paths?
- No: We're not finding shortest paths between nodes. Instead, we need to determine which nodes are reachable from crystal points and identify unreachable components.
Does the problem involve connectivity?
- Yes: The core of the problem is about connectivity - we need to ensure every focus point is either a crystal source or is reachable (connected) from some other point through directed edges.
Disjoint Set Union?
- Not the best fit: While DSU could handle connectivity in undirected graphs, our problem involves directed graphs where we need to track reachability in a specific direction. We need to identify strongly connected components and handle directed connectivity.
Since we're dealing with directed graph connectivity and need to explore reachability from crystal points, DFS is the appropriate choice. The solution uses:
- BFS/DFS to mark all nodes reachable from crystal points
- DFS to find and process unreachable components in topological order
- For each unreachable component, we add one edge to make it reachable
Conclusion: The flowchart leads us to use DFS for exploring directed graph connectivity and identifying components that need additional runes to satisfy the spell requirements.
Intuition
Let's think about what makes a focus point "valid" for the spell. A focus point is valid if it either has a crystal (energy source) or receives magic from another point (has incoming flow). The key insight is that crystal points are our starting sources - they're automatically valid and can spread their magic to other points through directed runes.
First, we can identify all points that are already valid by starting from crystal points and following the directed runes to see which points can be reached. Any point reachable from a crystal point will receive magic flow, making it valid. We can use BFS or DFS from all crystal points to mark these reachable nodes.
After this marking phase, we'll have some focus points that are still invalid - they don't have crystals and can't be reached from any crystal point through existing runes. These unreachable points form one or more disconnected components in our graph.
Here's the critical observation: for each disconnected component of invalid points, we only need to add one rune to make the entire component valid. If we add a rune pointing to any node in the component, that node becomes valid (it now receives flow), and from there, the existing runes within the component can propagate validity to other nodes.
But which node in each component should receive the new rune? The trick is to process nodes in reverse topological order. By using DFS to build a topological ordering of the unreachable nodes and then processing them in reverse, we ensure that when we add a rune to make one node valid, we can propagate that validity through the component as efficiently as possible.
The algorithm becomes:
- Mark all nodes reachable from crystals as valid
- Find all invalid nodes and perform DFS to get their topological order
- Process invalid nodes in reverse topological order
- For each still-invalid node encountered, add one rune to it (incrementing our answer) and mark all nodes reachable from it as valid
This greedy approach ensures we add the minimum number of runes because we're maximizing the coverage of each added rune by choosing roots that can validate entire components.
Learn more about Depth-First Search, Breadth-First Search, Union Find, Graph and Topological Sort patterns.
Solution Approach
The implementation follows our intuition with three main phases: marking reachable nodes, finding unreachable components, and adding minimum runes.
Phase 1: Build the graph and mark nodes reachable from crystals
First, we construct an adjacency list representation of the directed graph:
g = [[] for _ in range(n)]
for a, b in zip(flowFrom, flowTo):
g[a].append(b)
Then we mark all crystal nodes and perform BFS to find all nodes reachable from them:
vis = [0] * n # 0: unvisited, 1: reachable from crystals, 2: visited in DFS for x in crystals: vis[x] = 1 bfs(deque(crystals))
The bfs
function explores all nodes reachable from the initial queue of crystal nodes, marking them with vis[b] = 1
:
def bfs(q: Deque[int]):
while q:
a = q.popleft()
for b in g[a]:
if vis[b] == 1:
continue
vis[b] = 1
q.append(b)
Phase 2: Find topological ordering of unreachable nodes
For all nodes that are still unvisited (vis[i] == 0
), we perform DFS to build a topological ordering:
seq = []
for i in range(n):
if vis[i] == 0:
dfs(i)
seq.reverse()
The dfs
function marks nodes with vis[a] = 2
and appends them to seq
in post-order:
def dfs(a: int):
vis[a] = 2
for b in g[a]:
if vis[b] > 0:
continue
dfs(b)
seq.append(a)
By reversing seq
, we get the nodes in reverse topological order, ensuring that when we process a node, all nodes it can reach come later in the sequence.
Phase 3: Add minimum runes
We iterate through the nodes in reverse topological order. For each node still marked as unreachable (vis[i] == 2
), we:
- Add one rune pointing to it (increment
ans
) - Mark it and all nodes reachable from it as valid using BFS
ans = 0 for i in seq: if vis[i] == 2: q = deque([i]) vis[i] = 1 bfs(q) ans += 1
This greedy approach ensures minimum runes because:
- Each added rune makes an entire component valid through the BFS propagation
- Processing in reverse topological order maximizes the coverage of each rune
- We only add a rune when we encounter a component root that hasn't been made valid yet
The time complexity is O(n + m)
where m
is the number of edges, as we perform graph traversals a constant number of times. The space complexity is O(n + m)
for storing the graph and auxiliary data structures.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to illustrate the solution approach.
Example Setup:
n = 6
focus points (numbered 0-5)crystals = [0]
(only point 0 has a crystal)flowFrom = [0, 1, 3]
andflowTo = [1, 2, 4]
- This creates edges: 0β1, 1β2, 3β4
Initial Graph State:
Crystal point: [0] Edges: 0 β 1 β 2 3 β 4 5 (isolated)
Phase 1: Mark nodes reachable from crystals
Starting from crystal point 0, we perform BFS:
- Visit 0 (crystal), mark as valid (vis[0] = 1)
- From 0, reach 1, mark as valid (vis[1] = 1)
- From 1, reach 2, mark as valid (vis[2] = 1)
After Phase 1: vis = [1, 1, 1, 0, 0, 0]
- Points 0, 1, 2 are valid (reachable from crystal)
- Points 3, 4, 5 are still invalid
Phase 2: Find topological ordering of unreachable nodes
Now we perform DFS on unreachable nodes (3, 4, 5):
- DFS from 3: visit 3, then 4, mark both with vis=2
- Post-order adds to seq: [4, 3]
- DFS from 5: visit 5, mark with vis=2
- Post-order adds to seq: [4, 3, 5]
Reverse the sequence: seq = [5, 3, 4]
After Phase 2: vis = [1, 1, 1, 2, 2, 2]
Phase 3: Add minimum runes
Process nodes in order [5, 3, 4]:
-
Process node 5: vis[5] = 2 (still invalid)
- Add a rune pointing to 5 (ans = 1)
- Mark 5 as valid (vis[5] = 1)
- BFS from 5: no outgoing edges, so only 5 is affected
-
Process node 3: vis[3] = 2 (still invalid)
- Add a rune pointing to 3 (ans = 2)
- Mark 3 as valid (vis[3] = 1)
- BFS from 3: reach 4, mark it as valid (vis[4] = 1)
-
Process node 4: vis[4] = 1 (already valid from step 2)
- Skip, no rune needed
Final Result:
- We added 2 runes total
- All nodes are now valid:
- 0: has crystal
- 1, 2: reachable from crystal point 0
- 3: receives flow from new rune
- 4: reachable from 3
- 5: receives flow from new rune
The algorithm correctly identified that we needed one rune for the component {3, 4} and one rune for the isolated node {5}, giving us the minimum of 2 additional runes.
Solution Implementation
1from typing import List
2from collections import deque
3
4class Solution:
5 def minRunesToAdd(
6 self, n: int, crystals: List[int], flowFrom: List[int], flowTo: List[int]
7 ) -> int:
8 def bfs(queue: deque[int]) -> None:
9 """
10 Breadth-first search to mark all nodes reachable from the queue.
11 Updates visited array to mark nodes as reachable (value 1).
12 """
13 while queue:
14 current_node = queue.popleft()
15 for neighbor in adjacency_list[current_node]:
16 if visited[neighbor] == 1:
17 continue
18 visited[neighbor] = 1
19 queue.append(neighbor)
20
21 def dfs(node: int) -> None:
22 """
23 Depth-first search for topological sorting.
24 Marks nodes as processed (value 2) and builds reverse topological order.
25 """
26 visited[node] = 2
27 for neighbor in adjacency_list[node]:
28 if visited[neighbor] > 0:
29 continue
30 dfs(neighbor)
31 topological_order.append(node)
32
33 # Build adjacency list representation of the directed graph
34 adjacency_list = [[] for _ in range(n)]
35 for source, destination in zip(flowFrom, flowTo):
36 adjacency_list[source].append(destination)
37
38 # Initialize queue with crystal nodes and mark them as visited
39 queue = deque(crystals)
40 visited = [0] * n # 0: unvisited, 1: reachable from crystals, 2: processed in DFS
41 for crystal_node in crystals:
42 visited[crystal_node] = 1
43
44 # Find all nodes reachable from initial crystals
45 bfs(queue)
46
47 # Perform DFS on unreachable nodes to get topological ordering
48 topological_order = []
49 for node in range(n):
50 if visited[node] == 0:
51 dfs(node)
52
53 # Reverse to get correct topological order (from sources to sinks)
54 topological_order.reverse()
55
56 # Count minimum runs needed by processing nodes in topological order
57 min_runs = 0
58 for node in topological_order:
59 if visited[node] == 2: # Node was unreachable and needs a crystal
60 queue = deque([node])
61 visited[node] = 1
62 bfs(queue) # Mark all nodes reachable from this new crystal
63 min_runs += 1
64
65 return min_runs
66
1class Solution {
2 // Array to track visit status: 0 = unvisited, 1 = powered, 2 = visited but not powered
3 private int[] visitStatus;
4 // Adjacency list representing the directed graph of power flow
5 private List<Integer>[] adjacencyList;
6 // List to store nodes in topological order (post-order DFS traversal)
7 private List<Integer> topologicalOrder = new ArrayList<>();
8
9 public int minRunesToAdd(int n, int[] crystals, int[] flowFrom, int[] flowTo) {
10 // Initialize the adjacency list for the directed graph
11 adjacencyList = new List[n];
12 Arrays.setAll(adjacencyList, i -> new ArrayList<>());
13
14 // Build the directed graph from flowFrom to flowTo
15 for (int i = 0; i < flowFrom.length; i++) {
16 adjacencyList[flowFrom[i]].add(flowTo[i]);
17 }
18
19 // Initialize BFS queue and visit status array
20 Deque<Integer> bfsQueue = new ArrayDeque<>();
21 visitStatus = new int[n];
22
23 // Mark all initial crystal nodes as powered and add them to BFS queue
24 for (int crystal : crystals) {
25 visitStatus[crystal] = 1; // 1 indicates powered
26 bfsQueue.offer(crystal);
27 }
28
29 // Perform BFS to propagate power from initial crystals
30 bfs(bfsQueue);
31
32 // Perform DFS on all unpowered nodes to build topological ordering
33 for (int node = 0; node < n; node++) {
34 if (visitStatus[node] == 0) { // If node is unvisited
35 dfs(node);
36 }
37 }
38
39 // Process nodes in reverse topological order to minimize additional runes
40 int additionalRunes = 0;
41 for (int i = topologicalOrder.size() - 1; i >= 0; i--) {
42 int currentNode = topologicalOrder.get(i);
43
44 // If node is visited but not powered, we need to add a rune here
45 if (visitStatus[currentNode] == 2) {
46 visitStatus[currentNode] = 1; // Mark as powered
47 bfsQueue.clear();
48 bfsQueue.offer(currentNode);
49 bfs(bfsQueue); // Propagate power from this new rune
50 additionalRunes++;
51 }
52 }
53
54 return additionalRunes;
55 }
56
57 /**
58 * BFS to propagate power through the graph
59 * Marks all reachable nodes from powered nodes as powered
60 */
61 private void bfs(Deque<Integer> queue) {
62 while (!queue.isEmpty()) {
63 int currentNode = queue.poll();
64
65 // Visit all neighbors and propagate power
66 for (int neighbor : adjacencyList[currentNode]) {
67 if (visitStatus[neighbor] == 1) { // Skip if already powered
68 continue;
69 }
70 visitStatus[neighbor] = 1; // Mark as powered
71 queue.offer(neighbor);
72 }
73 }
74 }
75
76 /**
77 * DFS to traverse unpowered nodes and build topological ordering
78 * Marks nodes as visited (but not powered) and adds them to topological order
79 */
80 private void dfs(int node) {
81 visitStatus[node] = 2; // Mark as visited but not powered
82
83 // Recursively visit all unvisited neighbors
84 for (int neighbor : adjacencyList[node]) {
85 if (visitStatus[neighbor] > 0) { // Skip if already visited or powered
86 continue;
87 }
88 dfs(neighbor);
89 }
90
91 // Add node to topological order (post-order)
92 topologicalOrder.add(node);
93 }
94}
95
1class Solution {
2public:
3 vector<int> visited; // Node visit status: 0=unvisited, 1=activated, 2=visited but not activated
4 vector<vector<int>> graph; // Adjacency list representation of the graph
5 vector<int> topologicalOrder; // Stores nodes in reverse topological order
6
7 int minRunesToAdd(int n, vector<int>& crystals, vector<int>& flowFrom, vector<int>& flowTo) {
8 // Build the adjacency list from edge information
9 graph.resize(n);
10 for (int i = 0; i < flowFrom.size(); ++i) {
11 graph[flowFrom[i]].push_back(flowTo[i]);
12 }
13
14 // Initialize BFS queue and mark initial crystal nodes as activated
15 deque<int> bfsQueue;
16 visited.resize(n, 0);
17 for (int crystalNode : crystals) {
18 visited[crystalNode] = 1; // Mark as activated
19 bfsQueue.push_back(crystalNode);
20 }
21
22 // Propagate activation from initial crystal nodes
23 performBFS(bfsQueue);
24
25 // Perform DFS on all unvisited nodes to build topological ordering
26 for (int node = 0; node < n; ++node) {
27 if (visited[node] == 0) {
28 performDFS(node);
29 }
30 }
31
32 // Process nodes in reverse topological order to find minimum activations needed
33 int additionalActivations = 0;
34 for (int i = topologicalOrder.size() - 1; i >= 0; --i) {
35 int currentNode = topologicalOrder[i];
36
37 // If node was visited but not activated, we need to activate it
38 if (visited[currentNode] == 2) {
39 visited[currentNode] = 1; // Activate the node
40 bfsQueue.clear();
41 bfsQueue.push_back(currentNode);
42 performBFS(bfsQueue); // Propagate activation from this node
43 ++additionalActivations;
44 }
45 }
46
47 return additionalActivations;
48 }
49
50private:
51 // BFS to propagate activation through the graph
52 void performBFS(deque<int>& bfsQueue) {
53 while (!bfsQueue.empty()) {
54 int currentNode = bfsQueue.front();
55 bfsQueue.pop_front();
56
57 // Process all neighbors of current node
58 for (int neighbor : graph[currentNode]) {
59 // Skip if neighbor is already activated
60 if (visited[neighbor] == 1) {
61 continue;
62 }
63 visited[neighbor] = 1; // Mark neighbor as activated
64 bfsQueue.push_back(neighbor);
65 }
66 }
67 }
68
69 // DFS to build topological ordering and mark nodes as visited
70 void performDFS(int currentNode) {
71 visited[currentNode] = 2; // Mark as visited but not activated
72
73 // Recursively visit all unvisited neighbors
74 for (int neighbor : graph[currentNode]) {
75 if (visited[neighbor] > 0) { // Skip if already visited or activated
76 continue;
77 }
78 performDFS(neighbor);
79 }
80
81 // Add to topological order after processing all descendants
82 topologicalOrder.push_back(currentNode);
83 }
84};
85
1/**
2 * Calculates the minimum number of runs needed to add crystals to cover all reachable nodes
3 * @param n - Total number of nodes in the graph
4 * @param crystals - Array of initial crystal positions
5 * @param flowFrom - Array of edge source nodes
6 * @param flowTo - Array of edge destination nodes
7 * @returns Minimum number of additional crystal placements needed
8 */
9function minRunesToAdd(
10 n: number,
11 crystals: number[],
12 flowFrom: number[],
13 flowTo: number[],
14): number {
15 // Build adjacency list representation of the directed graph
16 const adjacencyList: number[][] = Array.from({ length: n }, () => []);
17 for (let i = 0; i < flowFrom.length; i++) {
18 const sourceNode: number = flowFrom[i];
19 const targetNode: number = flowTo[i];
20 adjacencyList[sourceNode].push(targetNode);
21 }
22
23 // Initialize visited array: 0 = unvisited, 1 = has crystal, 2 = visited in DFS
24 const visited: number[] = Array(n).fill(0);
25
26 // Mark initial crystal positions as visited
27 for (const crystalNode of crystals) {
28 visited[crystalNode] = 1;
29 }
30
31 /**
32 * Performs BFS to mark all nodes reachable from the given starting nodes
33 * @param queue - Initial queue of nodes to start BFS from
34 */
35 const bfs = (queue: number[]): void => {
36 while (queue.length > 0) {
37 const currentNode: number = queue.shift()!;
38
39 // Explore all neighbors of current node
40 for (const neighbor of adjacencyList[currentNode]) {
41 // Skip if neighbor already has a crystal
42 if (visited[neighbor] === 1) continue;
43
44 // Mark neighbor as having crystal and add to queue
45 visited[neighbor] = 1;
46 queue.push(neighbor);
47 }
48 }
49 };
50
51 // Array to store nodes in reverse topological order
52 const topologicalSequence: number[] = [];
53
54 /**
55 * Performs DFS to generate reverse topological ordering of unvisited nodes
56 * @param currentNode - Current node being processed in DFS
57 */
58 const dfs = (currentNode: number): void => {
59 // Mark current node as visited in DFS
60 visited[currentNode] = 2;
61
62 // Recursively visit all unvisited neighbors
63 for (const neighbor of adjacencyList[currentNode]) {
64 if (visited[neighbor] > 0) continue;
65 dfs(neighbor);
66 }
67
68 // Add current node to sequence after processing all descendants
69 topologicalSequence.push(currentNode);
70 };
71
72 // First BFS: Mark all nodes reachable from initial crystals
73 bfs(crystals);
74
75 // DFS on remaining unvisited nodes to get topological ordering
76 for (let nodeIndex = 0; nodeIndex < n; nodeIndex++) {
77 if (visited[nodeIndex] === 0) {
78 dfs(nodeIndex);
79 }
80 }
81
82 // Reverse to get correct topological order (sources first)
83 topologicalSequence.reverse();
84
85 // Count minimum crystals needed by processing nodes in topological order
86 let additionalCrystalsNeeded: number = 0;
87 for (const node of topologicalSequence) {
88 // If node was visited by DFS but not reached by any crystal
89 if (visited[node] === 2) {
90 // Place a crystal here and mark all reachable nodes
91 bfs([node]);
92 visited[node] = 1;
93 additionalCrystalsNeeded++;
94 }
95 }
96
97 return additionalCrystalsNeeded;
98}
99
Time and Space Complexity
Time Complexity: O(n + m)
where n
is the number of nodes and m
is the number of edges (size of flowFrom/flowTo arrays).
- Building the adjacency list
g
takesO(m)
time - Initial BFS from crystal nodes visits each node at most once and traverses each edge at most once:
O(n + m)
- DFS to find topological ordering visits each unvisited node exactly once and each edge from unvisited nodes once:
O(n + m)
- Processing nodes in topological order: each node can trigger at most one BFS, and across all BFS calls, each node is visited at most once and each edge is traversed at most once:
O(n + m)
- Overall:
O(m) + O(n + m) + O(n + m) + O(n + m) = O(n + m)
Space Complexity: O(n + m)
- Adjacency list
g
stores all edges:O(m)
- Visited array
vis
:O(n)
- Queue
q
can contain at mostn
nodes:O(n)
- Sequence array
seq
stores at mostn
nodes:O(n)
- Recursion stack for DFS can go up to
O(n)
depth in worst case - Overall:
O(m) + O(n) + O(n) + O(n) + O(n) = O(n + m)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrectly handling strongly connected components (SCCs)
A critical pitfall is assuming that adding a single rune to any node in an unreachable strongly connected component will satisfy all nodes in that component. Consider this scenario:
- Nodes A, B, C form a cycle: AβBβCβA
- None of them have crystals or incoming edges from crystal-reachable nodes
Wrong assumption: Adding a rune to node A will make B and C valid since A can reach them.
The problem: This creates a logical contradiction. For A to provide magic to B and C, A itself needs to have magic. But A only gets magic from the rune we're adding to it. However, the rune pointing to A doesn't automatically give A the ability to be a magic source - it just means A receives magic. The cycle doesn't have an actual magic source.
Solution: The code correctly handles this by:
- Using BFS after marking a node as reachable (setting
visited[node] = 1
) - This simulates that once we add a rune to a node, it becomes "powered" and can propagate magic through its outgoing edges
- The topological ordering ensures we process SCCs efficiently
2. Not processing nodes in the correct order
Pitfall: Processing unreachable nodes in arbitrary order might lead to suboptimal solutions.
Consider this graph:
- Node A β Node B β Node C (all unreachable from crystals)
- If we process B first and add a rune to it, then process A and add another rune to it, we use 2 runes
- But if we process A first, adding a rune to A would make B and C reachable through propagation, requiring only 1 rune
Solution: The code uses reverse topological ordering (topological_order.reverse()
), which ensures:
- We process "upstream" nodes before "downstream" nodes
- When we add a rune to a node, we maximize the number of other nodes that become reachable
- This greedy approach guarantees the minimum number of runes
3. Confusing the visited states
Pitfall: Using a simple boolean visited array can lead to incorrect results because nodes have three distinct states:
- Reachable from crystals (satisfied, no rune needed)
- Unreachable but processed in DFS (needs consideration)
- Completely unvisited
Wrong approach:
visited = [False] * n # Binary state - loses information
Solution: The code uses a tri-state system:
visited = [0] * n # 0: unvisited, 1: reachable from crystals, 2: processed in DFS
This allows distinguishing between nodes that are already satisfied (value 1) and those that still need runes (value 2) after the DFS phase.
4. Forgetting to propagate reachability after adding a rune
Pitfall: Simply counting unreachable components without considering that adding a rune to one node can satisfy many others through existing edges.
Wrong approach:
# Incorrect: just counting unreachable nodes
for node in range(n):
if not reachable[node]:
min_runs += 1
Solution: After conceptually adding a rune to a node, perform BFS to mark all nodes reachable from it:
if visited[node] == 2: queue = deque([node]) visited[node] = 1 bfs(queue) # Propagate reachability through existing edges min_runs += 1
This ensures that one rune can satisfy multiple nodes if they're connected, minimizing the total count.
Which of the following is a min heap?
Recommended Readings
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Union Find Disjoint Set Union Data Structure Introduction Prerequisite Depth First Search Review problems dfs_intro Once we have a strong grasp of recursion and Depth First Search we can now introduce Disjoint Set Union DSU This data structure is motivated by the following problem Suppose we have sets of elements
Want a Structured Path to Master System Design Too? Donβt Miss This!