3528. Unit Conversion I
Problem Description
You have n
types of units, numbered from 0
to n - 1
. These units can be converted into one another based on specific rules given by a 2D integer array conversions
, which has n - 1
entries. Each entry conversions[i] = [sourceUnit_i, targetUnit_i, conversionFactor_i]
indicates that one unit of sourceUnit_i
can be converted into conversionFactor_i
units of targetUnit_i
.
The task is to compute an array baseUnitConversion
of length n
. This array quantifies how many units of each type are equivalent to a single unit of type 0
. Due to potentially large numbers, each baseUnitConversion[i]
should be returned modulo 10^9 + 7
.
Intuition
The problem can be visualized as a tree-like conversion structure, where unit 0
is the root and all other units are connected through conversion paths defined by the conversions
array. Since there are n - 1
conversion paths for n
units, the relationship can be uniquely traversed from unit 0
to any other unit.
We use Depth-First Search (DFS) to navigate this structure. Starting from unit 0
, each conversion link in the path can be regarded as an edge leading to another unit with a specific conversion factor. We utilize an adjacency list to represent these relationships, which helps efficiently traverse from any given unit to units it can convert into, along with the associated conversion factors.
One begins DFS from the root 0
, with an initial multiplication factor of 1
, as unit 0
is equivalent to itself. As the DFS progresses, the function updates the equivalent conversion factors in the baseUnitConversion
array. Precisely, when moving from unit s
to unit t
with conversion factor w
, the product of the current multiplier and w
(modulo 10^9 + 7
) is passed to subsequent recursive calls. This effectively calculates how many units of each type are equivalent to one unit of type 0
, concluding with the return of the assembled result.
Learn more about Depth-First Search, Breadth-First Search and Graph patterns.
Solution Approach
The solution employs a Depth-First Search (DFS) strategy to traverse the conversion tree starting from unit 0
. This method leverages recursive traversal to compute the conversion factors for all units relative to unit 0
.
Here's a step-by-step breakdown of the implementation:
-
Initialization:
- Define a constant
mod
as10^9 + 7
to ensure that large numbers remain manageable through modulo operations. - Determine
n
, the total number of units, which is one more than the length of theconversions
array (n = len(conversions) + 1
).
- Define a constant
-
Graph Representation:
- Construct an adjacency list
g
of sizen
to represent conversion relationships. Each entryg[i]
will store tuples(t, w)
, wheret
is the target unit andw
is the conversion factor from uniti
to unitt
.
- Construct an adjacency list
-
Populating the Graph:
- Iterate over each conversion in
conversions
. For each conversion[s, t, w]
, add(t, w)
tog[s]
, establishing a directed edge representing the conversion froms
tot
with factorw
.
- Iterate over each conversion in
-
Depth-First Search (DFS) Function:
- Implement a recursive DFS function
dfs(s: int, mul: int) -> None
:s
is the current unit being processed.mul
is the conversion multiplier from unit0
to units
.- Assign
mul
toans[s]
, indicating the equivalent conversion factor for units
. - For each adjacent unit
t
connected tos
throughg[s]
, recursively calldfs(t, mul * w % mod)
, updating the multiplier based on the conversion factorw
.
- Implement a recursive DFS function
-
Solution Execution:
- Initialize the
ans
array with zeros, designed to store conversion factors for all units. - Invoke
dfs(0, 1)
to start traversal from unit0
with a multiplier of1
.
- Initialize the
-
Result:
- Upon completion of the DFS traversal,
ans
contains the conversion factors for each unit in terms of unit0
, modulated bymod
.
- Upon completion of the DFS traversal,
This algorithm efficiently computes the required conversion factors by exploring each conversion path using DFS, ensuring clarity and accuracy in determining how units relate to the base unit 0
.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider a small example with n = 3
units and conversions defined as follows:
conversions = [[0, 1, 2], [1, 2, 3]]
These indicate that:
- One unit of type
0
converts to2
units of type1
. - One unit of type
1
converts to3
units of type2
.
We'll use these conversions to compute the baseUnitConversion
array, which represents how many units of each type are equivalent to one unit of type 0
, using the DFS approach described in the solution.
Step-by-Step Execution:
-
Initialization:
- Set the
mod
constant to10^9 + 7
. - Calculate
n = len(conversions) + 1 = 3
.
- Set the
-
Graph Representation:
- Create an adjacency list
g
withn
empty lists:g = [[], [], []]
.
- Create an adjacency list
-
Populating the Graph:
- For the conversion
[0, 1, 2]
, add(1, 2)
tog[0]
:g = [[(1, 2)], [], []]
. - For the conversion
[1, 2, 3]
, add(2, 3)
tog[1]
:g = [[(1, 2)], [(2, 3)], []]
.
- For the conversion
-
Depth-First Search (DFS) Function:
- Define a recursive DFS function. Initialize
ans = [0, 0, 0]
. - Start DFS with
dfs(0, 1)
:- Set
ans[0] = 1
. - Process
g[0] = [(1, 2)]
: for(1, 2)
, calldfs(1, 1 * 2 % mod)
:- Set
ans[1] = 2
(conversion factor from unit0
to unit1
is2
). - Process
g[1] = [(2, 3)]
: for(2, 3)
, calldfs(2, 2 * 3 % mod)
:- Set
ans[2] = 6
(conversion factor from unit0
to unit2
is6
).
- Set
- Set
- Set
- Define a recursive DFS function. Initialize
-
Result:
- The
ans
array is[1, 2, 6]
. - This means:
- 1 unit of type
0
is equivalent to 1 unit of type0
. - 2 units of type
1
are equivalent to 1 unit of type0
. - 6 units of type
2
are equivalent to 1 unit of type0
.
- 1 unit of type
- The
This walkthrough illustrates how DFS recursively calculates each unit's conversion factor relative to unit 0
, providing the complete baseUnitConversion
array.
Solution Implementation
1from typing import List
2
3class Solution:
4 def baseUnitConversions(self, conversions: List[List[int]]) -> List[int]:
5 # Perform Depth-First Search to compute conversion multipliers
6 def dfs(source: int, multiplier: int) -> None:
7 # Store the computed multiplier for the current unit
8 result[source] = multiplier
9 # Traverse each connected unit from the current unit
10 for target, weight in graph[source]:
11 # Calculate the new multiplier and perform DFS recursively
12 dfs(target, (multiplier * weight) % MOD)
13
14 MOD = 10**9 + 7 # Modulo value for large number handling
15 n = len(conversions) + 1 # Total number of base units
16 graph = [[] for _ in range(n)] # Graph representation for units and their conversion rates
17
18 # Build the graph based on conversions
19 for source, target, weight in conversions:
20 graph[source].append((target, weight))
21
22 result = [0] * n # Array to store conversion multipliers for each unit
23 dfs(0, 1) # Start DFS from base unit 0 with initial multiplier 1
24
25 return result
26
1import java.util.ArrayList;
2import java.util.Arrays;
3import java.util.List;
4
5class Solution {
6 // Constant to define the modulo value for calculations
7 private final int MOD = (int) 1e9 + 7;
8
9 // Adjacency list to represent the graph of conversions
10 private List<int[]>[] graph;
11
12 // Array to store the conversion results
13 private int[] result;
14
15 // Variable to store the number of nodes (units)
16 private int numUnits;
17
18 // Method to compute base unit conversions given the conversion relationships
19 public int[] baseUnitConversions(int[][] conversions) {
20 // Initialize the number of units to the number of conversions plus one
21 numUnits = conversions.length + 1;
22
23 // Initialize the graph to hold the list of conversions for each unit
24 graph = new List[numUnits];
25 Arrays.setAll(graph, k -> new ArrayList<>());
26
27 // Initialize the result array to store final conversion values
28 result = new int[numUnits];
29
30 // Populate the graph with conversion information between units
31 for (int[] conversion : conversions) {
32 // Each conversion is represented as [fromUnit, toUnit, conversionFactor]
33 graph[conversion[0]].add(new int[] {conversion[1], conversion[2]});
34 }
35
36 // Start the depth-first search from the base unit (0) with an initial multiplier of 1
37 dfs(0, 1);
38
39 // Return the array containing all conversion results
40 return result;
41 }
42
43 // Recursive Depth First Search (DFS) to traverse the graph and calculate conversion factors
44 private void dfs(int source, long multiplier) {
45 // Set the conversion result for the current unit
46 result[source] = (int) multiplier;
47
48 // Traverse all adjacent units from the current unit
49 for (int[] edge : graph[source]) {
50 // Perform DFS on the adjacent unit, updating the conversion factor
51 dfs(edge[0], multiplier * edge[1] % MOD);
52 }
53 }
54}
55
1#include <vector>
2#include <utility>
3
4class Solution {
5public:
6 // This method performs a depth-first search to calculate and return the
7 // conversion values of base units given a list of conversion rules.
8 std::vector<int> baseUnitConversions(std::vector<std::vector<int>>& conversions) {
9 const int mod = 1e9 + 7; // Define a constant for modulo operations.
10 int n = conversions.size() + 1; // Determine the number of nodes (units).
11
12 // Create an adjacency list for the conversion graph.
13 std::vector<std::vector<std::pair<int, int>>> graph(n);
14
15 // Initialize the result vector with size n to hold conversion values.
16 std::vector<int> results(n);
17
18 // Populate the adjacency list with conversion pairs and weights.
19 for (const auto& conversion : conversions) {
20 graph[conversion[0]].push_back({conversion[1], conversion[2]});
21 }
22
23 // Lambda function for depth-first search with self-reference to traverse the graph.
24 auto dfs = [&](auto&& dfs, int source, long long accumulated_value) -> void {
25 results[source] = accumulated_value; // Set the conversion value for the current node.
26 for (auto [target, weight] : graph[source]) {
27 // Recursively apply DFS to adjacent nodes with updated conversion values.
28 dfs(dfs, target, accumulated_value * weight % mod);
29 }
30 };
31
32 dfs(dfs, 0, 1); // Start DFS from the root node (index 0) with initial conversion value of 1.
33 return results; // Return the result vector containing conversion values for all units.
34 }
35};
36
1// Defines the conversion function for base units.
2function baseUnitConversions(conversions: number[][]): number[] {
3 // Define the modulo value for large number computations.
4 const mod = BigInt(1e9 + 7);
5
6 // Calculate the size of the graph based on the number of conversions.
7 const n = conversions.length + 1;
8
9 // Initialize the graph as an adjacency list.
10 const graph: { to: number; weight: number }[][] = Array.from({ length: n }, () => []);
11
12 // Populate the graph with the conversions data.
13 for (const [source, target, weight] of conversions) {
14 graph[source].push({ to: target, weight });
15 }
16
17 // Initialize the answers array to store conversion results for each base unit.
18 const results: number[] = Array(n).fill(0);
19
20 // Depth-first search function for traversing the graph.
21 const dfs = (currentNode: number, currentMultiplier: number) => {
22 // Set the current node's conversion result.
23 results[currentNode] = currentMultiplier;
24
25 // Traverse the adjacency list for the current node.
26 for (const { to, weight } of graph[currentNode]) {
27 // Calculate new multiplier and continue DFS on the child node.
28 dfs(to, Number((BigInt(currentMultiplier) * BigInt(weight)) % mod));
29 }
30 };
31
32 // Begin DFS from the root node with an initial multiplier of 1.
33 dfs(0, 1);
34
35 // Return the array of conversion results.
36 return results;
37}
38
Time and Space Complexity
The time complexity of the code is O(n)
, where n
is the number of units. This is because the DFS traversal visits each node exactly once, and there are n
nodes corresponding to n
units.
The space complexity of the code is also O(n)
, where n
is the number of units. This is due to the space needed to store the adjacency list g
and the ans
list, both of which have size proportional to the number of units n
.
Learn more about how to find time and space complexity quickly.
What is the best way of checking if an element exists in an unsorted array once in terms of time complexity? Select the best that applies.
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
https assets algo monster cover_photos graph svg Graph Fundamentals Tree with 0 cycle At this point you should be pretty familiar with trees A tree is a special kind of graph a connected acyclic cycle less graph A graph may contain cycle s and nodes could be disconnected A tree
Want a Structured Path to Master System Design Too? Don’t Miss This!