To solve this problem using the Disjoint Set Union (DSU) approach, we aim to union nodes based on their parent-child relationships. A valid tree should follow these rules:
Time Complexity: O(n), where n is the number of nodes, due to each union and find operation being nearly constant with path compression.
Space Complexity: O(n) for the parent array.
1class Solution:
2 def find(self, parent, x):
3 if parent[x] != x:
4 parent[x] = self.find(parent, parent[x])
5 return parent[x]
6
7 def union(self, parent, x, y):
8 rootX = self.find(parent, x)
9 rootY = self.find(parent, y)
10 if rootX == rootY:
11 return False
12 parent[rootY] = rootX
13 return True
14
15 def validateBinaryTreeNodes(self, n, leftChild, rightChild):
16 parent = list(range(n))
17 hasParent = [0] * n
18
19 for i in range(n):
20 if leftChild[i] != -1:
21 if hasParent[leftChild[i]] or not self.union(parent, i, leftChild[i]):
22 return False
23 hasParent[leftChild[i]] = 1
24 if rightChild[i] != -1:
25 if hasParent[rightChild[i]] or not self.union(parent, i, rightChild[i]):
26 return False
27 hasParent[rightChild[i]] = 1
28
29 return hasParent.count(0) == 1
The Python solution follows the union-find pattern, organized into find
and union
methods. The parent
list captures each node’s representative root, while hasParent
ensures a node has at most one parent. Disjoint set operations verify no cycles and ensure all nodes unify under one connected root without duplication errors, confirming only one root node.
This approach involves calculating the in-degree of each node and checking connectivity via a DFS. The key aspects of a tree like single-root presence and cycle-checking can be managed by:
Time Complexity: O(n), since each node and its immediate edges are evaluated once in each step, including in-drives calculations and DFS.
Space Complexity: O(n) for holding visited tracking and in-degree counts.
1class Solution {
2 private void dfs(int node, boolean[] visited, int[][] children) {
3 if (visited[node]) return;
4 visited[node] = true;
5 if (children[0][node] != -1) dfs(children[0][node], visited, children);
6 if (children[1][node] != -1) dfs(children[1][node], visited, children);
7 }
8
9 public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) {
10 int[] inDegree = new int[n];
11 int root = -1;
12
13 for (int i = 0; i < n; ++i) {
14 if (leftChild[i] != -1) inDegree[leftChild[i]]++;
15 if (rightChild[i] != -1) inDegree[rightChild[i]]++;
16 }
17
18 for (int i = 0; i < n; ++i) {
19 if (inDegree[i] == 0) {
20 if (root == -1) {
21 root = i;
22 } else {
23 return false;
24 }
25 }
26 }
27
28 if (root == -1) return false;
29
30 boolean[] visited = new boolean[n];
31 dfs(root, visited, new int[][]{leftChild, rightChild});
32
33 for (boolean v : visited) {
34 if (!v) return false;
35 }
36
37 return true;
38 }
39}
The Java version checks for a single node with inDegree
zero, identifying it as the root. Nodes are structured as positions within two arrays/lists representing binary tree branches. The DFS aims to explore all nodes in one pass, validating complete connectivity without omitted nodes.