617. Merge Two Binary Trees - Explanation

Problem Link

Description

You are given two binary trees root1 and root2.

Imagine that when you put one of them to cover the other, some nodes of the two trees are overlapped while the others are not. You need to merge the two trees into a new binary tree. The merge rule is that if two nodes overlap, then sum node values up as the new value of the merged node. Otherwise, the NOT null node will be used as the node of the new tree.

Return the merged tree.

Note: The merging process must start from the root nodes of both trees.

Example 1:

Input: root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]

Output: [3,4,5,5,4,null,7]

Example 2:

Input: root1 = [1], root2 = [2]

Output: [3]

Example 3:

Input: root1 = [], root2 = [2]

Output: [2]

Constraints:

  • 0 <= The number of nodes in both the trees <= 2000.
  • -10,000 <= Node.val <= 10,000


Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Binary Trees - Understanding tree node structure with left and right children
  • Depth First Search (DFS) - Recursively or iteratively traversing tree structures
  • Recursion - Solving problems by breaking them into smaller subproblems with base cases
  • Stack - Using explicit stacks to convert recursive solutions to iterative ones

1. Depth First Search (Creating New Tree)

Intuition

To merge two trees, we traverse both trees simultaneously. At each position, if both trees have a node, we create a new node with the sum of their values. If only one tree has a node at a position, we use that node's value. We recursively build the left and right subtrees the same way. This approach creates an entirely new tree without modifying the inputs.

Algorithm

  1. If both root1 and root2 are null, return null.
  2. Get the values from each tree (use 0 if the node is null).
  3. Create a new node with the sum of the two values.
  4. Recursively merge the left children and assign to root.left.
  5. Recursively merge the right children and assign to root.right.
  6. Return the new root node.
# 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 mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
        if not root1 and not root2:
            return None

        v1 = root1.val if root1 else 0
        v2 = root2.val if root2 else 0
        root = TreeNode(v1 + v2)

        root.left = self.mergeTrees(root1.left if root1 else None, root2.left if root2 else None)
        root.right = self.mergeTrees(root1.right if root1 else None, root2.right if root2 else None)

        return root

Time & Space Complexity

  • Time complexity: O(m+n)O(m + n)
  • Space complexity:
    • O(m+n)O(m + n) space for recursion stack.
    • O(m+n)O(m + n) space for the output.

Where mm and nn are the number of nodes in the given trees.


2. Depth First Search (In Place)

Intuition

Instead of creating new nodes, we can modify the first tree in place. If one tree is missing at a position, we simply return the other tree's subtree. If both exist, we add the second tree's value to the first tree's node and recursively merge the children. This saves memory by reusing existing nodes.

Algorithm

  1. If root1 is null, return root2.
  2. If root2 is null, return root1.
  3. Add root2.val to root1.val.
  4. Recursively merge the left subtrees and assign to root1.left.
  5. Recursively merge the right subtrees and assign to root1.right.
  6. Return root1.
# 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 mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
        if not root1:
            return root2
        if not root2:
            return root1

        root1.val += root2.val
        root1.left = self.mergeTrees(root1.left, root2.left)
        root1.right = self.mergeTrees(root1.right, root2.right)
        return root1

Time & Space Complexity

  • Time complexity: O(min(m,n))O(min(m, n))
  • Space complexity: O(min(m,n))O(min(m, n)) for recursion stack.

Where mm and nn are the number of nodes in the given trees.


3. Iterative DFS (Creating New Tree)

Intuition

We can avoid recursion by using an explicit stack to traverse both trees. We push triplets of (node1, node2, merged_node) onto the stack. For each triplet, we create children for the merged node based on whether the corresponding children exist in either input tree. This creates a new tree while simulating the recursive approach iteratively.

Algorithm

  1. Handle base cases where one or both trees are null.
  2. Create a new root node with the sum of both root values.
  3. Initialize a stack with the triplet (root1, root2, root).
  4. While the stack is not empty:
    • Pop (node1, node2, node).
    • For left children: if either exists, create a merged left child and push to stack if both exist.
    • For right children: similarly handle and push if both exist.
  5. Return the merged root.
# 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 mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
        if not root1:
            return root2
        if not root2:
            return root1

        root = TreeNode(root1.val + root2.val)
        stack = [(root1, root2, root)]

        while stack:
            node1, node2, node = stack.pop()

            if node1.left and node2.left:
                node.left = TreeNode(node1.left.val + node2.left.val)
                stack.append((node1.left, node2.left, node.left))
            elif not node1.left:
                node.left = node2.left
            else:
                node.left = node1.left

            if node1.right and node2.right:
                node.right = TreeNode(node1.right.val + node2.right.val)
                stack.append((node1.right, node2.right, node.right))
            elif not node1.right:
                node.right = node2.right
            else:
                node.right = node1.right

        return root

Time & Space Complexity

  • Time complexity: O(m+n)O(m + n)
  • Space complexity:
    • O(m+n)O(m + n) space for the stack.
    • O(m+n)O(m + n) space for the output.

Where mm and nn are the number of nodes in the given trees.


4. Iterative DFS (In Place)

Intuition

Similar to the recursive in-place approach, we can modify the first tree iteratively using a stack. We push pairs of nodes from both trees. When both nodes exist, we add values and continue processing children. If only one tree has a child at a position, we directly attach that subtree to the first tree. This avoids creating new nodes.

Algorithm

  1. Handle base cases where one or both trees are null.
  2. Initialize a stack with the pair (root1, root2).
  3. While the stack is not empty:
    • Pop (node1, node2). Skip if either is null.
    • Add node2.val to node1.val.
    • If both have left children, push them to the stack. Otherwise, if node1 has no left child, attach node2.left.
    • Similarly handle right children.
  4. Return root1.
# 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 mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
        if not root1:
            return root2
        if not root2:
            return root1

        stack = [(root1, root2)]

        while stack:
            node1, node2 = stack.pop()
            if not node1 or not node2:
                continue

            node1.val += node2.val

            if node1.left and node2.left:
                stack.append((node1.left, node2.left))
            elif not node1.left:
                node1.left = node2.left

            if node1.right and node2.right:
                stack.append((node1.right, node2.right))
            elif not node1.right:
                node1.right = node2.right

        return root1

Time & Space Complexity

  • Time complexity: O(min(m,n))O(min(m, n))
  • Space complexity: O(min(m,n))O(min(m, n)) for the stack.

Where mm and nn are the number of nodes in the given trees.


Common Pitfalls

Not Handling the Case When One Tree is Null

When one of the input trees is null at a position while the other is not, you should return the non-null subtree directly. Failing to handle this base case properly leads to null pointer exceptions or missing nodes in the result.

Modifying the Input Trees Unintentionally

When using the in-place approach, the first tree is modified to become the merged result. If the original trees need to be preserved for other purposes, this causes unexpected side effects. Use the approach that creates a new tree if the inputs must remain unchanged.