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

<ul>
	<li>Select two <strong>distinct</strong> indices <code>i</code> and <code>j</code> such that the value stored at one of the <strong>leaves </strong>of <code>trees[i]</code> is equal to the <strong>root value</strong> of <code>trees[j]</code>.</li>
	<li>Replace the leaf node in <code>trees[i]</code> with <code>trees[j]</code>.</li>
	<li>Remove <code>trees[j]</code> from <code>trees</code>.</li>
</ul>

<p>Return<em> the <strong>root</strong> of the resulting BST if it is possible to form a valid BST after performing </em><code>n - 1</code><em> operations, or</em><em> </em><code>null</code> <i>if it is impossible to create a valid BST</i>.</p>

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

<ul>
	<li>Every node in the node&#39;s left subtree has a value&nbsp;<strong>strictly less</strong>&nbsp;than the node&#39;s value.</li>
	<li>Every node in the node&#39;s right subtree has a value&nbsp;<strong>strictly greater</strong>&nbsp;than the node&#39;s value.</li>
</ul>

<p>A leaf is a node that has no children.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2021/06/08/d1.png" style="width: 450px; height: 163px;" />
<pre>
<strong>Input:</strong> trees = [[2,1],[3,2,5],[5,4]]
<strong>Output:</strong> [3,2,5,1,null,4]
<strong>Explanation:</strong>
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]].
<img alt="" src="https://assets.leetcode.com/uploads/2021/06/24/diagram.png" style="width: 450px; height: 181px;" />
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]].
<img alt="" src="https://assets.leetcode.com/uploads/2021/06/24/diagram-2.png" style="width: 220px; height: 165px;" />
The resulting tree, shown above, is a valid BST, so return its root.</pre>

<p><strong class="example">Example 2:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2021/06/08/d2.png" style="width: 450px; height: 171px;" />
<pre>
<strong>Input:</strong> trees = [[5,3,8],[3,2,6]]
<strong>Output:</strong> []
<strong>Explanation:</strong>
Pick i=0 and j=1 and merge trees[1] into trees[0].
Delete trees[1], so trees = [[5,3,8,2,6]].
<img alt="" src="https://assets.leetcode.com/uploads/2021/06/24/diagram-3.png" style="width: 240px; height: 196px;" />
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.
</pre>

<p><strong class="example">Example 3:</strong></p>
<img alt="" src="https://assets.leetcode.com/uploads/2021/06/08/d3.png" style="width: 430px; height: 168px;" />
<pre>
<strong>Input:</strong> trees = [[5,4],[3]]
<strong>Output:</strong> []
<strong>Explanation:</strong> It is impossible to perform any operations.
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>n == trees.length</code></li>
	<li><code>1 &lt;= n &lt;= 5 * 10<sup>4</sup></code></li>
	<li>The number of nodes in each tree is in the range <code>[1, 3]</code>.</li>
	<li>Each node in the input may have children but no grandchildren.</li>
	<li>No two roots of <code>trees</code> have the same value.</li>
	<li>All the trees in the input are <strong>valid BSTs</strong>.</li>
	<li><code>1 &lt;= TreeNode.val &lt;= 5 * 10<sup>4</sup></code>.</li>
</ul>

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def canMerge(self, trees: List[TreeNode]) -> Optional[TreeNode]:
        # one last try after 90 minutes

        # got a minor hint from votrubac: "Just do what it says"

        # let's not over-think this

        # first up:
        #    trees: each is a node with 1-2 children, basically an edge list
        #           all root values are distinct
        
        # so we can follow root -> child and child->val to another tree, and so on

        # edge cases:
        #   not a BST: we can check if it's a BST with DFS
        #   cycles: covered by checking if it's a BST, if there's a loop then we'll find parent -> ... -> parent
        #           and the latter encounter of parent will not be within the exclusive value bounds required
        #
        #   not all nodes are covered: need to start with a root
        #      > no incident edges
        #      > there should be only one root
        #          and it should be connected to all of the values

        # record all child values, i.e. all nodes that will have incident edges,
        # so we can scan `trees` for root candidates
        children = set()
        for t in trees:
            for c in [t.left, t.right]:
                if c is None: continue
                if c.val in children: 
                    return None # duplicate value, union of trees would have duplicates
                children.add(c.val)

        # check to make sure there's only one root; if we find zero or multiple we know
        # the linking will not form a single tree
        root = None
        for t in trees:
            if t.val in children: continue # not the overall root
            else:
                if root is None: root = t
                else: 
                    return None # found two nodes with no parents, and thus would be two trees in a forest at the end
        if root is None: 
            return None


        # now there's only one possible node:
        #    > it must be a BST
        #    > it must contain all of the trees
        #        (edge case: we can have a cycle plus a separate tree rooted at `root`)
        tree_dict = {t.val : t for t in trees}
        unseen = {t.val for t in trees}

        def checkBST(node: Optional[TreeNode], lo: float, hi: float) -> bool:
            """
            Returns True iff node is a BST whose values are in (lo, hi) exclusive.

            side effect: removes encountered nodes from the unseen set
            """
            if node is None: return True # empty tree

            if not (lo < node.val < hi):
                return False

            unseen.discard(node.val)

            node = tree_dict.get(node.val, node) # "promote" leaves to the tree with the same value if possible

            # note that the left children must have values < node.val, and right values > node.val
            if not checkBST(node.left, lo, node.val): return False
            if not checkBST(node.right, node.val, hi): return False
            return True

        if not checkBST(root, -math.inf, math.inf):
            return None

        if len(unseen):
            return None

        # finally: if we do all the links we now know it will be a BST, so do it

        def buildBST(node: Optional[TreeNode]) -> Optional[TreeNode]:
            if node is None: return None

            if node.val in tree_dict:
                # replace this leaf node with the tree version
                tree = tree_dict[node.val]

                tree.left = buildBST(tree.left)
                tree.right = buildBST(tree.right)
                return tree
            else:
                return node

        final = buildBST(root)

        return buildBST(root)
1371