Validate Binary Tree Nodes

Updated on 30 June, 2025
Validate Binary Tree Nodes header image

Problem Statement

Given n binary tree nodes, with each node indexed from 0 to n-1, along with two arrays leftChild and rightChild representing the indices of the left and right children of the nodes respectively, the challenge is to determine whether all these nodes form a single valid binary tree. Each index in the arrays can either point to another node, represented by a valid index, or indicate no child with a -1 value.

To form a valid single binary tree, several conditions must be fulfilled:

  1. The tree must have exactly one root. This means only one node should not have any parent.
  2. All nodes must be connected and accessible from this root.
  3. There should not be any cycles, ensuring it maintains the structure of a tree.

These criteria ensure there is a connected structure spanning all nodes without any repetitions or cycles, forming a tree topology according to binary tree properties.

Examples

Example 1

Input:

n = 4, leftChild = [1,-1,3,-1], rightChild = [2,-1,-1,-1]

Output:

true

Example 2

Input:

n = 4, leftChild = [1,-1,3,-1], rightChild = [2,3,-1,-1]

Output:

false

Example 3

Input:

n = 2, leftChild = [1,0], rightChild = [-1,-1]

Output:

false

Constraints

  • n == leftChild.length == rightChild.length
  • 1 <= n <= 104
  • -1 <= leftChild[i], rightChild[i] <= n - 1

Approach and Intuition

To verify if all nodes form a single valid binary tree, you can follow these steps:

  1. Identify the Root Node:

    • A node that doesn't appear as a child in both leftChild and rightChild is potentially a root. If you find more than one such node, or no such node at all, then the structure can't form a valid binary tree.
  2. Check for Connectivity and Cycle using DFS or BFS:

    • Start from the identified root node and use Depth-First Search (DFS) or Breadth-First Search (BFS) to traverse the tree. During the traversal, mark each node as visited.
    • If you re-encounter a visited node during traversal (not via its immediate parent), it indicates a cycle.
    • If after one full traversal starting from the root node, some nodes remain unvisited, the graph is not fully connected as required for a valid binary tree.
  3. Count the no. of Parent Links:

    • Ensure that no node is claimed as a child more than once across both leftChild and rightChild. If any node appears more than once as a child, it indicates an invalid structure with multiple parents, making it not a tree.

Examples in Practice:

  • Example 1:

    • For n = 4, leftChild = [1,-1,3,-1], rightChild = [2,-1,-1,-1], node 0 is the parent of nodes 1 and 2, and node 2 is the parent of node 3. All nodes connect through node 0 with no cycles and no isolated fragments, thus forming a valid single binary tree (true).
  • Example 2:

    • The input n = 4, leftChild = [1,-1,3,-1], rightChild = [2,3,-1,-1] introduces a cycle as both 0 and 2 claim 3 as a child. This invalidates the structure as a binary tree (false).
  • Example 3:

    • In the case of n = 2, leftChild = [1,0], rightChild = [-1,-1], a cycle exists between nodes 0 and 1, disqualifying the structure from being a single connected binary tree without cycles (false).

Solutions

  • C++
  • Java
  • Python
cpp
class DisjointSet {
public:
    int setSize;
    int elementCount;
    vector<int> parent;
    vector<int> rank;
    
    DisjointSet(int count) {
        elementCount = count;
        parent.resize(count);
        setSize = count;
    
        for (int i = 0; i < count; i++) {
            parent[i] = i;
        }
    }
    
    int findRoot(int element) {
        if (parent[element] != element) {
            parent[element] = findRoot(parent[element]);
        }
    
        return parent[element];
    }
    
    bool unionElements(int root1, int root2) {
        int findRoot1 = findRoot(root1);
        int findRoot2 = findRoot(root2);
    
        if (findRoot1 == findRoot2) {
            return false;
        }
    
        setSize--;
        parent[findRoot2] = findRoot1;
    
        return true;
    }
};
    
class Validator {
public:
    bool isValidBinaryTree(int count, vector<int>& leftChild, vector<int>& rightChild) {
        DisjointSet ds(count);
        for (int node = 0; node < count; node++) {
            int children[] = {leftChild[node], rightChild[node]};
            for (int child : children) {
                if (child == -1) continue;
    
                if (!ds.unionElements(node, child)) {
                    return false;
                }
            }
        }
    
        return ds.setSize == 1;
    }
};

This solution aims to validate if the provided node descriptions form a valid binary tree using C++. The approach leverages a Disjoint Set Union (DSU) data structure to keep track of and manage the tree’s node connectivity and ensure that there are no cycles and that the tree is indeed a single, connected component.

  • DisjointSet Class:

    • Initializes with the number of elements (count), setting up a parent array for union-find operations.
    • findRoot function performs path compression to efficiently find the representative item of the set containing element.
    • unionElements function unites two sets; returns false if both elements are from the same set (indicating a cycle).
  • Validator Class:

    • isValidBinaryTree function:
      • Installs an instance of DisjointSet using the node count.
      • Iterates over nodes using arrays leftChild and rightChild to track children. For each node:
        • Skips if child is -1 (indicating no child).
        • Tries to union the current node with its children using unionElements. If this method returns false, a cycle is detected or there's an extra component, hence the tree is not valid.
      • After processing all nodes, it checks if there’s exactly one set left in DisjointSet (i.e., setSize == 1).

Overall, the solution checks for the correctness of a binary tree setup by ensuring all nodes are part of one single connected component without cycles, using standard DSU operations enhanced with path compression.

java
class DisjointSet {
    private final int size;
    private final int[] parent;
    public int count;
        
    DisjointSet(int size) {
        this.size = size;
        parent = new int[size];
        count = size;
            
        for (int i = 0; i < size; i++) {
            parent[i] = i;
        }
    }
        
    public boolean merge(int root1, int root2) {
        int leader1 = find(root1);
        int leader2 = find(root2);
            
        if (leader2 != root2 || leader1 == leader2) {
            return false;
        }
            
        count--;
        parent[leader2] = leader1;
    
        return true;
    }
        
    private int find(int node) {
        if (parent[node] != node) {
            parent[node] = find(parent[node]);
        }
            
        return parent[node];
    }
}
    
class BinaryTreeValidator {
    public boolean isValidBinaryTree(int n, int[] leftChild, int[] rightChild) {
        DisjointSet ds = new DisjointSet(n);
        for (int node = 0; node < n; node++) {
            int[] children = {leftChild[node], rightChild[node]};
            for (int child : children) {
                if (child == -1) {
                    continue;
                }
                    
                if (!ds.merge(node, child)) {
                    return false;
                }
            }
        }
            
        return ds.count == 1;
    }
}

Using Java to solve the problem of validating binary tree nodes involves leveraging a combination of specialized data structures. Specifically, the DisjointSet, also known as the Union-Find structure, comes into play. Here's how the solution unfolds:

  • Implement a DisjointSet class:

    • This class utilizes an array, parent, to keep track of the leader or parent of each element.
    • A merge operation tries to unite two different trees. If either the targets are already in the same tree or if attempting to connect a node directly to itself, it returns false indicating an error in tree structure.
    • A find operation ensures with path compression, which optimizes the structure by making nodes point directly to the leader on subsequent searches.
  • Develop the BinaryTreeValidator class:

    • The method isValidBinaryTree initializes a new DisjointSet with the given number of nodes.
    • It then iterates over each node in the binary tree, considering both its left and right children.
    • For each possible child (that isn't a marked null value -1), attempt to perform a merge operation using the corresponding parent node.
    • If at any point a merge operation returns false, the function concludes the tree is invalid.
    • Finally, after processing all nodes, it checks if there's precisely one remaining separate tree in the disjoint set, suggesting that the tree is structurally correct without cycles or disconnected components.

This approach, with its use of a union-find structure, is highly efficient for large inputs, ensuring minimal complexity in validating the binary tree structure in terms of both time and space.

python
class DisjointSet:
    def __init__(self, size):
        self.count = size
        self.root = list(range(size))
            
    def merge(self, x, y):
        rootX = self.locateRoot(x)
        rootY = self.locateRoot(y)
            
        if rootY != y or rootX == rootY:
            return False
            
        self.count -= 1
        self.root[rootY] = rootX
            
        return True
    
    def locateRoot(self, index):
        if self.root[index] != index:
            self.root[index] = self.locateRoot(self.root[index])
            
        return self.root[index]
            
    
class Validator:
    def verifyTree(self, nodes_count: int, lChild: List[int], rChild: List[int]) -> bool:
        ds = DisjointSet(nodes_count)
        for index in range(nodes_count):
            for offspring in [lChild[index], rChild[index]]:
                if offspring == -1:
                    continue
    
                if not ds.merge(index, offspring):
                    return False
                    
        return ds.count == 1

The provided Python code defines a solution to verify the validity of a binary tree's structure given a number of nodes and their left and right children. The primary components of this code include a DisjointSet class and a Validator class.

  • DisjointSet Class:

    • This class is designed to manage the connectivity of nodes using a union-find algorithm. It helps in determining whether adding an edge (a parent-child relationship) forms a cycle, which is invalid in a binary tree.
    • Methods implemented in this class:
      • __init__(size): Initializes the class with the total number of nodes. Each node is its own separate set initially.
      • merge(x, y): Attempts to unite two sets corresponding to nodes x and y. If y is not its own root or x and y are already in the same set, it returns False, indicating an invalid tree structure, otherwise, it merges them and returns True.
      • locateRoot(index): Finds and returns the root of the set containing index, using path compression for efficiency.
  • Validator Class:

    • It contains the verifyTree method that uses an instance of the DisjointSet to verify the tree structure.
    • For each node, it processes its left and right children (given by the lists lChild and rChild). If a child exists (not equal to -1), it attempts to merge this child with its parent node.
    • The merge operations will return False immediately if any rule of the binary tree structure is violated (like having multiple parents or forming a cycle).
    • After processing all nodes, the method checks if there is exactly one set left, which would mean all nodes are connected correctly into a single tree without any separate or cyclic structures, returning True if valid.

In summary, this code seamlessly integrates a disjoint set data structure to effectively and efficiently verify the integrity and validity of a binary tree structure based on parent-child relationships provided for each node. Ensure all nodes are connected without forming cycles or having multiple parents for a tree structure to be considered valid.

Comments

No comments yet.