Binary Search TreesProblem 14 of 22

Count pairs from 2 BST whose sum is equal to given value X

Brute Force
Time: O(m*n)
Space: O(m+n)
Optimal
Time: O(m+n)
Space: O(h1+h2)

Problem Statement

Given two BSTs containing n1 and n2 distinct nodes respectively, count the number of pairs (one from each BST) whose sum equals a given value x.

Example:

BST 1: BST 2: 5 6 / \ / \ 3 7 4 8 / \ / \ 1 4 2 5 x = 10 Output: 3 Pairs: (5,5), (3,7) - wait, 7 is in BST1 not BST2 Actually: (4,6), (5,5), (2,8) - checking sums equal to 10 Pairs: (5,5)=10, (4,6)=10, (2,8)=10

Approach 1: Brute Force (Two Nested Traversals)

Intuition

For each node in BST1, search for (x - node.val) in BST2.

Algorithm

  1. Traverse all nodes in BST1
  2. For each node, search for complement (x - val) in BST2
  3. Count successful finds
java
class TreeNode {
    int val;
    TreeNode left, right;
    TreeNode(int x) { val = x; }
}

public class Solution {
    private boolean searchBST(TreeNode root, int target) {
        if (root == null) return false;
        if (root.val == target) return true;
        if (target < root.val) return searchBST(root.left, target);
        return searchBST(root.right, target);
    }
    
    private int count = 0;
    
    private void countPairsHelper(TreeNode root1, TreeNode root2, int x) {
        if (root1 == null) return;
        
        // Check if complement exists in BST2
        if (searchBST(root2, x - root1.val)) {
            count++;
        }
        
        countPairsHelper(root1.left, root2, x);
        countPairsHelper(root1.right, root2, x);
    }
    
    public int countPairs(TreeNode root1, TreeNode root2, int x) {
        count = 0;
        countPairsHelper(root1, root2, x);
        return count;
    }
}

Complexity Analysis

  • Time Complexity: O(m * n) or O(m * h2) - For each of m nodes, search in BST2
  • Space Complexity: O(h1 + h2) - Recursion stacks

Approach 2: Better (Using HashSet)

Intuition

Store all values from one BST in a HashSet. For each node in the other BST, check if complement exists in the set.

Algorithm

  1. Traverse BST1 and store all values in HashSet
  2. Traverse BST2 and check if (x - val) exists in HashSet
  3. Count matches
java
import java.util.*;

class TreeNode {
    int val;
    TreeNode left, right;
    TreeNode(int x) { val = x; }
}

public class Solution {
    private void storeInorder(TreeNode root, Set<Integer> set) {
        if (root == null) return;
        storeInorder(root.left, set);
        set.add(root.val);
        storeInorder(root.right, set);
    }
    
    private int countWithSet(TreeNode root, Set<Integer> set, int x) {
        if (root == null) return 0;
        
        int count = set.contains(x - root.val) ? 1 : 0;
        
        return count + countWithSet(root.left, set, x) + countWithSet(root.right, set, x);
    }
    
    public int countPairsHashSet(TreeNode root1, TreeNode root2, int x) {
        Set<Integer> set = new HashSet<>();
        storeInorder(root1, set);
        return countWithSet(root2, set, x);
    }
}

Complexity Analysis

  • Time Complexity: O(m + n) - Linear traversal of both trees
  • Space Complexity: O(m) or O(n) - HashSet storage

Approach 3: Optimal (Two Pointer with BST Iterators)

Intuition

Use two pointers technique similar to finding pairs in sorted arrays. Use BST iterators - one going forward (smallest to largest) on BST1 and one going backward (largest to smallest) on BST2.

Algorithm

  1. Create forward iterator for BST1 (inorder)
  2. Create backward iterator for BST2 (reverse inorder)
  3. Use two pointers: if sum < x, move forward; if sum > x, move backward
  4. Count when sum equals x
java
import java.util.*;

class TreeNode {
    int val;
    TreeNode left, right;
    TreeNode(int x) { val = x; }
}

class BSTIterator {
    private Stack<TreeNode> stack;
    private boolean reverse;
    
    public BSTIterator(TreeNode root, boolean isReverse) {
        stack = new Stack<>();
        reverse = isReverse;
        pushAll(root);
    }
    
    private void pushAll(TreeNode node) {
        while (node != null) {
            stack.push(node);
            node = reverse ? node.right : node.left;
        }
    }
    
    public int next() {
        TreeNode curr = stack.pop();
        pushAll(reverse ? curr.left : curr.right);
        return curr.val;
    }
    
    public boolean hasNext() {
        return !stack.isEmpty();
    }
}

public class Solution {
    public int countPairsOptimal(TreeNode root1, TreeNode root2, int x) {
        if (root1 == null || root2 == null) return 0;
        
        BSTIterator iter1 = new BSTIterator(root1, false);
        BSTIterator iter2 = new BSTIterator(root2, true);
        
        int count = 0;
        int val1 = iter1.next();
        int val2 = iter2.next();
        
        while (true) {
            int sum = val1 + val2;
            
            if (sum == x) {
                count++;
                if (!iter1.hasNext() || !iter2.hasNext()) break;
                val1 = iter1.next();
                val2 = iter2.next();
            } else if (sum < x) {
                if (!iter1.hasNext()) break;
                val1 = iter1.next();
            } else {
                if (!iter2.hasNext()) break;
                val2 = iter2.next();
            }
        }
        
        return count;
    }
}

Complexity Analysis

  • Time Complexity: O(m + n) - Each node visited at most once
  • Space Complexity: O(h1 + h2) - Stack space for iterators

Key Takeaways

  1. Two pointer technique: Apply to BSTs using iterators
  2. BST Iterator: Powerful pattern for BST problems
  3. Forward + Backward: Mimics sorted array two-pointer approach
  4. HashSet trade-off: O(m+n) time but O(m) or O(n) space
  5. Optimal approach uses only O(h1+h2) space
  6. Similar pattern for finding pair with given sum in single BST
  7. Can be extended to count pairs with sum in a range