Unit Conversion I
MedPrompt
There are n types of units indexed from 0 to n - 1. You are given a 2D integer array conversions of length n - 1, where conversions[i] = [sourceUniti, targetUniti, conversionFactori]. This indicates that a single unit of type sourceUniti is equivalent to conversionFactori units of type targetUniti.
Return an array baseUnitConversion of length n, where baseUnitConversion[i] is the number of units of type i equivalent to a single unit of type 0. Since the answer may be large, return each baseUnitConversion[i] modulo 109 + 7.
Example 1:
Input: conversions = [[0,1,2],[1,2,3]]
Output: [1,2,6]
Explanation:
- Convert a single unit of type 0 into 2 units of type 1 using
conversions[0]. - Convert a single unit of type 0 into 6 units of type 2 using
conversions[0], thenconversions[1].

Example 2:
Input: conversions = [[0,1,2],[0,2,3],[1,3,4],[1,4,5],[2,5,2],[4,6,3],[5,7,4]]
Output: [1,2,3,8,10,6,30,24]
Explanation:
- Convert a single unit of type 0 into 2 units of type 1 using
conversions[0]. - Convert a single unit of type 0 into 3 units of type 2 using
conversions[1]. - Convert a single unit of type 0 into 8 units of type 3 using
conversions[0], thenconversions[2]. - Convert a single unit of type 0 into 10 units of type 4 using
conversions[0], thenconversions[3]. - Convert a single unit of type 0 into 6 units of type 5 using
conversions[1], thenconversions[4]. - Convert a single unit of type 0 into 30 units of type 6 using
conversions[0],conversions[3], thenconversions[5]. - Convert a single unit of type 0 into 24 units of type 7 using
conversions[1],conversions[4], thenconversions[6].
Constraints:
2 <= n <= 105conversions.length == n - 10 <= sourceUniti, targetUniti < n1 <= conversionFactori <= 109- It is guaranteed that unit 0 can be converted into any other unit through a unique combination of conversions without using any conversions in the opposite direction.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach tackles the problem by directly answering the question for each unit one by one. For every unit i (from 1 to n-1), we want to find the conversion factor from unit 0. We can achieve this by finding the path from unit 0 to unit i in the conversion graph and multiplying the factors along that path. A graph traversal algorithm like Breadth-First Search (BFS) or Depth-First Search (DFS) can be used to find this path. This entire process is repeated for all n-1 units, leading to a lot of repeated work.
Algorithm
- Build Graph: Represent the conversions as a directed graph. An adjacency list is a suitable data structure, where
adj[u]stores a list of pairs(v, factor), indicating that1 unit of u = factor units of v. - Initialize Result: Create an array
baseUnitConversionof sizen. SetbaseUnitConversion[0] = 1. - Iterate and Search: For each unit
ifrom 1 ton-1:- Perform a graph traversal (e.g., BFS) starting from unit 0 to find unit
i. - During the BFS, keep track of the cumulative conversion factor from the start node (0) to the current node.
- When a node
uwith cumulative factorF_uis processed, and it has a neighborvwith conversion factorw, the cumulative factor forvis(F_u * w) % MOD. - Once unit
iis found, its calculated cumulative factor is the answer. Store this inbaseUnitConversion[i].
- Perform a graph traversal (e.g., BFS) starting from unit 0 to find unit
- Return Result: Return the
baseUnitConversionarray.
Walkthrough
The algorithm proceeds as follows:
- First, we build a graph representation of the unit conversions. An adjacency list is a good choice, where for each conversion
[u, v, w], we add a directed edge fromutovwith weightw. - We initialize our result array,
baseUnitConversion, of sizen. We knowbaseUnitConversion[0]is 1. - Then, we loop through every other unit
ifrom 1 ton-1. - Inside the loop, for each
i, we start a fresh Breadth-First Search (BFS) from unit 0. The goal of this BFS is to find the path and the corresponding cumulative conversion factor to reachi. - The BFS queue will store pairs of
[node, cumulativeFactor]. We start by adding[0, 1]to the queue. - When we explore from a node
uwith factorF_uto its neighborvvia an edge with weightw, the new cumulative factor forvis(F_u * w) % MOD. - When our BFS reaches the target node
i, we record its computed cumulative factor inbaseUnitConversion[i]and can terminate that specific search. - After iterating through all
i, thebaseUnitConversionarray is complete and can be returned.
import java.util.*; class Solution { private static final int MOD = 1_000_000_007; public long[] unitConversion(int n, int[][] conversions) { List<List<int[]>> adj = new ArrayList<>(); for (int i = 0; i < n; i++) { adj.add(new ArrayList<>()); } for (int[] conv : conversions) { adj.get(conv[0]).add(new int[]{conv[1], conv[2]}); } long[] baseUnitConversion = new long[n]; baseUnitConversion[0] = 1; for (int i = 1; i < n; i++) { // For each target unit 'i', run a separate BFS from 0 Queue<long[]> bfsQueue = new LinkedList<>(); // {node, cumulativeFactor} bfsQueue.offer(new long[]{0, 1}); boolean[] visited = new boolean[n]; visited[0] = true; while(!bfsQueue.isEmpty()){ long[] current = bfsQueue.poll(); int u = (int)current[0]; long uFactor = current[1]; if(u == i){ baseUnitConversion[i] = uFactor; break; // Found the path to i, can stop this BFS } for(int[] edge : adj.get(u)){ int v = edge[0]; int factor = edge[1]; if(!visited[v]){ visited[v] = true; long vFactor = (uFactor * factor) % MOD; bfsQueue.offer(new long[]{v, vFactor}); } } } } return baseUnitConversion; }}Complexity
Time
O(n^2) - We iterate through `n-1` target units. For each unit, we perform a graph traversal (BFS/DFS) which takes O(V+E) = O(n) time in the worst case. This results in a total time complexity of O(n * n).
Space
O(n) - The adjacency list requires O(n) space. For each of the n-1 traversals, the queue and visited array also require O(n) space.
Trade-offs
Pros
Conceptually simple and directly follows the problem's request for each unit.
Easy to implement without needing deeper graph theory insights.
Cons
Highly inefficient due to redundant computations. The same subpaths (e.g., from unit 0 to some intermediate unit) are traversed multiple times across the different searches.
Will likely result in a 'Time Limit Exceeded' error for large inputs due to its quadratic time complexity.
This optimal approach leverages the key insight from the problem constraints: the n units and n-1 conversions form a tree structure rooted at unit 0. This is because there are n nodes, n-1 edges, and every node is reachable from node 0, which defines a tree. This structure guarantees a unique, directed path from unit 0 to every other unit. Instead of finding each path individually, we can find all of them simultaneously in a single graph traversal (either DFS or BFS) starting from the root (unit 0).
Algorithm
- Build Graph: Construct an adjacency list representation of the directed graph from the
conversionsarray.adj[u]will contain pairs of(v, factor). - Initialize: Create the
baseUnitConversionarray of sizen. InitializebaseUnitConversion[0] = 1. - Single Traversal (BFS):
- Create a queue and add the root node, 0.
- While the queue is not empty:
- Dequeue the current unit,
u. - For each neighbor
vofuwith conversion factorw:- Calculate
baseUnitConversion[v] = (baseUnitConversion[u] * w) % MOD. - Enqueue the neighbor
vto be processed later.
- Calculate
- Dequeue the current unit,
- Single Traversal (DFS Alternative):
- Define a recursive function, say
calculateFactors(u). - Inside the function, iterate through all neighbors
vofuwith conversion factorw. - For each neighbor, calculate
baseUnitConversion[v] = (baseUnitConversion[u] * w) % MOD. - Make a recursive call:
calculateFactors(v). - Start the process by setting
baseUnitConversion[0] = 1and then callingcalculateFactors(0).
- Define a recursive function, say
- Return Result: After the single traversal is complete, the
baseUnitConversionarray will be fully populated. Return it.
Walkthrough
The core idea is to perform one traversal that calculates the conversion factors for all units at once. We start at unit 0, whose conversion factor relative to itself is 1.
- Graph Representation: We first build an adjacency list from the
conversionsarray. For each[source, target, factor], we store an edge fromsourcetotargetwith the givenfactor. - Initialization: We create our result array,
baseUnitConversion, and setbaseUnitConversion[0] = 1. - Single Traversal: We can use either BFS or DFS.
- Using BFS: We initialize a queue with the root node, 0. We then enter a loop that continues as long as the queue is not empty. In each step, we dequeue a unit
u. We already know its conversion factor,baseUnitConversion[u]. For each of its childrenv(connected by a conversion with factorw), we can compute its conversion factor from unit 0 by extending the path fromu. The new factor isbaseUnitConversion[v] = (baseUnitConversion[u] * w) % MOD. We then enqueuevso that its children can be processed. Since it's a tree, we don't need avisitedarray; each node will be reached exactly once. - Using DFS: We can define a recursive function
dfs(u). We start by callingdfs(0). Inside the function for nodeu, we iterate through its childrenv. For each child, we calculate its factorbaseUnitConversion[v]based onbaseUnitConversion[u]and the connecting factor, and then make a recursive calldfs(v).
- Using BFS: We initialize a queue with the root node, 0. We then enter a loop that continues as long as the queue is not empty. In each step, we dequeue a unit
Both traversal methods ensure that every node and edge is visited exactly once, making the solution highly efficient.
BFS Implementation:
import java.util.*; class Solution { private static final int MOD = 1_000_000_007; public long[] unitConversion(int n, int[][] conversions) { List<List<int[]>> adj = new ArrayList<>(); for (int i = 0; i < n; i++) { adj.add(new ArrayList<>()); } for (int[] conv : conversions) { adj.get(conv[0]).add(new int[]{conv[1], conv[2]}); } long[] baseUnitConversion = new long[n]; baseUnitConversion[0] = 1; Queue<Integer> queue = new LinkedList<>(); queue.offer(0); while (!queue.isEmpty()) { int u = queue.poll(); for (int[] edge : adj.get(u)) { int v = edge[0]; int factor = edge[1]; baseUnitConversion[v] = (baseUnitConversion[u] * factor) % MOD; queue.offer(v); } } return baseUnitConversion; }}Complexity
Time
O(n) - Building the adjacency list takes O(n) time. The single DFS or BFS traversal visits each of the `n` units and `n-1` conversions exactly once. Thus, the total time complexity is linear with respect to the number of units.
Space
O(n) - The adjacency list requires O(n) space to store the n-1 conversions. The result array also takes O(n). The queue for BFS or the recursion stack for DFS can take up to O(n) space in the worst case (a skewed tree).
Trade-offs
Pros
Optimal time complexity, making it highly efficient and suitable for large constraints.
Elegant solution that correctly models the problem's underlying tree structure.
Avoids all redundant calculations by visiting each node and edge only once.
Cons
Requires the insight that the problem can be modeled as a tree traversal from a single root, which might not be immediately obvious.
Solutions
Solution
class Solution {private final int mod = (int)1 e9 + 7;private List<int[]>[] g;private int[] ans;private int n;public int[] baseUnitConversions(int[][] conversions) { n = conversions.length + 1; g = new List[n]; Arrays.setAll(g, k->new ArrayList<>()); ans = new int[n]; for (var e : conversions) { g[e[0]].add(new int[]{e[1], e[2]}); } dfs(0, 1); return ans; }private void dfs(int s, long mul) { ans[s] = (int)mul; for (var e : g[s]) { dfs(e[0], mul * e[1] % mod); } }}class Solution {public: vector<int> baseUnitConversions(vector<vector<int>> &conversions) { const int mod = 1e9 + 7; int n = conversions.size() + 1; vector<vector<pair<int, int>>> g(n); vector<int> ans(n); for (const auto &e : conversions) { g[e[0]].push_back({e[1], e[2]}); } auto dfs = [&](this auto &&dfs, int s, long long mul) -> void { ans[s] = mul; for (auto [t, w] : g[s]) { dfs(t, mul * w % mod); } }; dfs(0, 1); return ans; }};class Solution: def baseUnitConversions(self, conversions: List[List[int]]) -> List[int]: def dfs(s: int, mul: int) -> None: ans[s] = mul for t, w in g[s]: dfs(t, mul * w % mod) mod = 10 ** 9 + 7 n = len(conversions) + 1 g = [[] for _ in range(n)] for s, t, w in conversions: g[s]. append((t, w)) ans = [0] * n dfs(0, 1) return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.