{x}
blog image

Merge BSTs to Create Single BST

You are given n BST (binary search tree) root nodes for n separate BSTs stored in an array trees (0-indexed). Each BST in trees has at most 3 nodes, and no two roots have the same value. In one operation, you can:

  • Select two distinct indices i and j such that the value stored at one of the leaves of trees[i] is equal to the root value of trees[j].
  • Replace the leaf node in trees[i] with trees[j].
  • Remove trees[j] from trees.

Return the root of the resulting BST if it is possible to form a valid BST after performing n - 1 operations, or null if it is impossible to create a valid BST.

A BST (binary search tree) is a binary tree where each node satisfies the following property:

  • Every node in the node's left subtree has a value strictly less than the node's value.
  • Every node in the node's right subtree has a value strictly greater than the node's value.

A leaf is a node that has no children.

 

Example 1:

Input: trees = [[2,1],[3,2,5],[5,4]]
Output: [3,2,5,1,null,4]
Explanation:
In the first operation, pick i=1 and j=0, and merge trees[0] into trees[1].
Delete trees[0], so trees = [[3,2,5,1],[5,4]].

In the second operation, pick i=0 and j=1, and merge trees[1] into trees[0].
Delete trees[1], so trees = [[3,2,5,1,null,4]].

The resulting tree, shown above, is a valid BST, so return its root.

Example 2:

Input: trees = [[5,3,8],[3,2,6]]
Output: []
Explanation:
Pick i=0 and j=1 and merge trees[1] into trees[0].
Delete trees[1], so trees = [[5,3,8,2,6]].

The resulting tree is shown above. This is the only valid operation that can be performed, but the resulting tree is not a valid BST, so return null.

Example 3:

Input: trees = [[5,4],[3]]
Output: []
Explanation: It is impossible to perform any operations.

 

Constraints:

  • n == trees.length
  • 1 <= n <= 5 * 104
  • The number of nodes in each tree is in the range [1, 3].
  • Each node in the input may have children but no grandchildren.
  • No two roots of trees have the same value.
  • All the trees in the input are valid BSTs.
  • 1 <= TreeNode.val <= 5 * 104.

Solution Explanation: Merging BSTs to Create a Single BST

This problem requires merging multiple Binary Search Trees (BSTs) with at most three nodes each into a single valid BST. The core challenge lies in determining the feasibility of merging and ensuring the resulting tree adheres to BST properties.

Approach:

The solution employs a recursive depth-first search (DFS) approach combined with backtracking to explore all possible merging sequences.

  1. Base Case: If only one BST remains, it's the final result.

  2. Recursive Step: For each pair of BSTs, we check if a merge is possible. A merge is possible if a leaf node in one BST matches the root of another.

  3. Merge Operation: If a merge is possible, we perform it:

    • We replace the leaf node in the first BST with the entire second BST.
    • We remove the second BST from the list.
    • We recursively call the function to continue merging.
  4. Backtracking: If a merge doesn't lead to a valid final BST, we backtrack (undo the merge) and explore other possibilities.

  5. Validation: After each potential merge, we validate if the resulting tree is a valid BST using a helper function. A valid BST ensures that all nodes in the left subtree are smaller and all nodes in the right subtree are larger than the current node.

Code (Python):

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right
 
def is_valid_bst(root):
    def helper(node, min_val, max_val):
        if not node:
            return True
        if not (min_val < node.val < max_val):
            return False
        return helper(node.left, min_val, node.val) and helper(node.right, node.val, max_val)
    return helper(root, float('-inf'), float('inf'))
 
def merge_bsts(trees):
    def solve(trees):
        if len(trees) == 1:
            return trees[0]
        for i in range(len(trees)):
            for j in range(len(trees)):
                if i == j:
                    continue
                
                tree1 = trees[i]
                tree2 = trees[j]
 
                #Attempt merges
                if merge_possible(tree1,tree2):
                   new_tree = merge_trees(tree1,tree2)
                   new_trees = [t for k,t in enumerate(trees) if k !=j]
                   new_trees[i] = new_tree
                   result = solve(new_trees)
                   if result:
                       return result
        return None
 
 
    def merge_possible(tree1, tree2):
      #Check leaves of tree1 against root of tree2
      q = [tree1]
      while q:
          curr = q.pop(0)
          if not curr.left and not curr.right and curr.val == tree2.val:
            return True
          if curr.left:
            q.append(curr.left)
          if curr.right:
            q.append(curr.right)
      return False
 
    def merge_trees(tree1,tree2):
      q = [tree1]
      while q:
          curr = q.pop(0)
          if not curr.left and not curr.right and curr.val == tree2.val:
              curr.left = tree2
              return tree1
          if curr.left:
            q.append(curr.left)
          if curr.right:
            q.append(curr.right)
      return None
 
 
    result = solve(trees)
    return result if is_valid_bst(result) else None
 

Time Complexity: O(N * 3^N), where N is the number of trees. In the worst case, we may explore all possible merging sequences. Each merge operation takes constant time. The is_valid_bst function takes linear time in the size of the tree, and the tree's size grows with each merge.

Space Complexity: O(N) in the worst case due to the recursion depth and the space needed to store the trees.

Other Languages: The core logic remains similar in Java, C++, or Go. You would need to adapt the TreeNode class and potentially the implementation details for merging and validation based on the specific language features. The time and space complexities would remain the same.