AlgoMaster Logo

Trim a Binary Search Tree

mediumFrequency6 min readUpdated June 23, 2026

Understanding the Problem

We have a BST and a range [low, high]. We need to remove every node whose value falls outside this range, and return the modified tree. The critical requirement is that the relative structure of the remaining nodes stays the same. If node A was an ancestor of node B before trimming, and both survive, then A must still be an ancestor of B after trimming.

Removing a node does not mean discarding its entire subtree. Consider a node with value 0 when low = 1. The node itself is too small, but its right child (say, value 2) might still be in range. We cannot drop a whole subtree based on one node. We have to keep the valid parts.

The BST property is what makes the pruning cheap. If a node's value is less than low, then its entire left subtree is also less than low, because every value in the left subtree is smaller than the node. So we can discard the node and its left subtree together, but the right subtree still needs checking. The mirror case applies when a node's value is greater than high.

Key Constraints:

  • 1 <= number of nodes <= 10^4 → The tree is non-empty and has at most 10,000 nodes. An O(n) traversal is well within limits.
  • root is a valid BST → The strict ordering (values are unique) is what lets us prune a whole subtree from a single comparison.
  • 0 <= low <= high <= 10^4 → The range is always valid, since low never exceeds high. There is no empty-range case to handle. All values fit in a 32-bit int, so no overflow concerns.

Approach 1: Recursive DFS

Intuition

Recursion fits this problem because trimming a subtree is the same problem as trimming the whole tree. At each node, we compare its value against the range and one of three cases applies.

If the node's value is less than low, the node is too small, and so is its entire left subtree (every value there is smaller than the node, which is already below low). We discard the node and its left subtree together and recurse only into the right subtree, returning whatever it produces. A node with value 0 might have a right child with value 5, and when low = 1 that 5 belongs in the answer.

The mirror case applies when the node's value exceeds high: the node and its entire right subtree are too large, so we recurse into the left subtree and return that.

If the node's value is within [low, high], we keep it, but its descendants can still be out of range. We recurse into both children, reassign the trimmed subtrees back to root.left and root.right, and return the node.

Each call resolves one node and delegates the rest. The reassignment on the return path rebuilds the tree from the bottom up: a parent always receives the already-trimmed version of its child.

Algorithm

  1. If the root is null, return null (base case).
  2. If root.val < low, the entire left subtree is also out of range. Recursively trim the right subtree and return the result.
  3. If root.val > high, the entire right subtree is also out of range. Recursively trim the left subtree and return the result.
  4. If root.val is within [low, high], keep the node. Recursively trim the left subtree (assign to root.left) and the right subtree (assign to root.right).
  5. Return the root.

Example Walkthrough

1Visit node 3: value in [1,3], keep it. Trim both subtrees.
3keep0214
1/6

Code

The recursion stack is the only extra space this uses, and it can reach O(n) on a skewed tree. The next approach removes the stack entirely and trims in place with O(1) extra space.

Approach 2: Iterative

Intuition

The iterative approach applies the same logic as the recursive one without a call stack, split into three phases.

Phase 1 finds a valid root. The current root might be out of range. If it is too small, the valid root can only be in the right subtree, so we move right. If it is too large, we move left. We repeat until we reach a node within [low, high] or run out of nodes.

After Phase 1, every remaining out-of-range node sits on one side: nodes below low can only be in the left subtree, and nodes above high can only be in the right subtree. That lets us clean each side with an independent walk.

Phase 2 walks down the left chain from the root. At each node, while its left child's value is less than low, that child and its own left subtree are too small, so we replace the child with its right subtree, which may still hold valid nodes. We repeat until the left child is in range, then descend.

Phase 3 mirrors Phase 2 on the right chain: while a right child exceeds high, replace it with its left subtree.

Algorithm

  1. Find a valid root: while root is not null and root's value is out of range, move to the appropriate child.
  2. Trim the left subtree: starting from root, walk down the left children. If any left child's value is less than low, replace it with its right child.
  3. Trim the right subtree: starting from root, walk down the right children. If any right child's value is greater than high, replace it with its left child.
  4. Return the root.

Example Walkthrough

1Phase 1: Check root (3). Value in [1,3], root is valid.
3valid root0214
1/5

Code