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.
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.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.
root.val < low, the entire left subtree is also out of range. Recursively trim the right subtree and return the result.root.val > high, the entire right subtree is also out of range. Recursively trim the left subtree and return the result.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).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.
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.
Why can Phase 2 ignore the right subtree entirely? The valid root r satisfies r.val >= low. By the BST property, every node in r's right subtree is greater than r, hence also >= low, so none of them can be too small. The only nodes below low lie in r's left chain. Phase 3 reasons symmetrically about nodes above high. This is what lets the two walks run independently without missing a node.
low, replace it with its right child.high, replace it with its left child.