Trim a Binary Search Tree solution and explanation
9 more videos on this problem
Trim a Binary Search Tree - Leetcode 669 - PythonNeetCode · 11:53 · 23K views
Trim a Binary Search Tree | Live Coding with Explanation | Leetcode #669Algorithms Made Easy · 5:02 · 6K views
Trim a Binary Search Tree | Leetcode 669 | BST | Day-15Ayushi Sharma · 13:52 · 4K views
(Old) Leetcode 669 - BST, Recursion | Trim BSTNideesh Terapalli · 6:00 · 4K views
Trim a Binary Search Tree || Leetcode 669 || Code + Explanation || C++Code with Alisha · 9:32 · 3K views
Trim a Binary Search Tree | LeetCode 669 | bst treeNaresh Gupta · 10:11 · 1K views
Trim a Binary Search Tree | Leetcode 669Coding Decoded · 9:22 · 1K views
Leetcode - Trim a Binary Search Tree (Python)Timothy H Chang · 4:03 · 611 views
Trim a Binary Search Tree | LeetCode 669 | Coders CampCoders Camp · 8:05 · 182 views
Leetcode Medium: 669. Trim a Binary Search Treeleetcode · 7:39 · 138 views
The problem
Given the root of a binary search tree and the lowest and highest boundaries as low and high, trim the tree so that all its elements lies in [low, high]. Trimming the tree should not change the relative structure of the elements that will remain in the tree (i.e., any node's descendant should remain a descendant). It can be proven that there is a unique answer.
Return the root of the trimmed binary search tree. Note that the root may change depending on the given bounds.
Example 1:
Input: root = [1,0,2], low = 1, high = 2 Output: [1,null,2]
Example 2:
Input: root = [3,0,4,null,2,null,null,1], low = 1, high = 3 Output: [3,2,null,1]
Constraints:
- The number of nodes in the tree is in the range
[1, 104]. 0 <= Node.val <= 104- The value of each node in the tree is unique.
rootis guaranteed to be a valid binary search tree.0 <= low <= high <= 104
Approach overview
Problem Overview: You receive the root of a binary search tree and two boundaries low and high. The task is to remove every node whose value falls outside this range while preserving the BST structure. The final tree must still satisfy the binary search tree property.
Approach 1: Recursive Trimming with BST Properties (O(n) time, O(h) space)
This approach directly uses the ordering guarantee of a BST. If a node value is smaller than low, the entire left subtree must also be smaller, so you skip it and continue trimming the right subtree. If a node value is greater than high, the entire right subtree can be discarded and you move to the left subtree. Otherwise the node is valid, so recursively trim both children and reconnect them. Each node is visited at most once using depth-first search, giving O(n) time where n is the number of nodes, and O(h) recursion stack space where h is the tree height.
Approach 2: Iterative Approach Using a Stack (O(n) time, O(h) space)
An iterative DFS avoids recursion by maintaining an explicit stack. First adjust the root until it falls within the valid range by moving right when the value is too small or left when it is too large. After fixing the root, traverse the tree with a stack and trim children in place. When a left child is below low, replace it with its right subtree; when a right child exceeds high, replace it with its left subtree. The algorithm still processes each node once, so the time complexity remains O(n). The stack holds at most O(h) nodes, matching the height of the tree.
Recommended for interviews: The recursive BST-based trimming solution is the expected answer in most interviews. It shows that you recognize how BST ordering lets you discard entire subtrees without scanning them individually. The iterative version is useful if the interviewer asks for a non-recursive DFS or wants to discuss stack-based tree traversal, but the recursive approach is usually shorter and easier to reason about under pressure.
Approach 1: Recursive Trimming with BST Properties
We can utilize the properties of a BST to perform a recursive traversal. The strategy here involves:
- If the current node's value is less than
low, we need to trim the left subtree and consider the right subtree. - If the current node's value is greater than
high, we trim the right subtree and consider the left subtree. - If the current node's value is within the range [
low,high], we recursively trim both subtrees.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode:
if not root:
return None
if root.val < low:
return self.trimBST(root.right, low, high)
if root.val > high:
return self.trimBST(root.left, low, high)
root.left = self.trimBST(root.left, low, high)
root.right = self.trimBST(root.right, low, high)
return rootclass TreeNode {
int val;
TreeNode left, right;
TreeNode(int x) { val = x; }
}
public class Solution {
public TreeNode trimBST(TreeNode root, int low, int high) {
if (root == null) return null;
if (root.val < low) return trimBST(root.right, low, high);
if (root.val > high) return trimBST(root.left, low, high);
root.left = trimBST(root.left, low, high);
root.right = trimBST(root.right, low, high);
return root;
}
}#include <iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
TreeNode* trimBST(TreeNode* root, int low, int high) {
if (!root) return NULL;
if (root->val < low) return trimBST(root->right, low, high);
if (root->val > high) return trimBST(root->left, low, high);
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
return root;
}function TreeNode(val) {
this.val = val;
this.left = this.right = null;
}
var trimBST = function(root, low, high) {
if (!root) return null;
if (root.val < low) return trimBST(root.right, low, high);
if (root.val > high) return trimBST(root.left, low, high);
root.left = trimBST(root.left, low, high);
root.right = trimBST(root.right, low, high);
return root;
};#include <stdio.h>
#include <stdlib.h>
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
};
struct TreeNode* trimBST(struct TreeNode* root, int low, int high) {
if (!root) return NULL;
if (root->val < low) return trimBST(root->right, low, high);
if (root->val > high) return trimBST(root->left, low, high);
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
return root;
}public class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int x) { val = x; }
}
public class Solution {
public TreeNode TrimBST(TreeNode root, int low, int high) {
if (root == null) return null;
if (root.val < low) return TrimBST(root.right, low, high);
if (root.val > high) return TrimBST(root.left, low, high);
root.left = TrimBST(root.left, low, high);
root.right = TrimBST(root.right, low, high);
return root;
}
}Python implementations directly return the recursive results based on the current node's value compared to the bounds.
Time Complexity: O(n)
Space Complexity: O(h)
Approach 2: Iterative Approach Using a Stack
This iterative approach uses a stack to traverse the tree. The main idea is to mimic the recursive depth-first search using an explicit stack.
- Push the right child first to ensure the left child is processed first (since stack is LIFO).
- Trim nodes from the stack if their values fall outside the [low, high] range.
- Keep adjusting the left and right pointers based on the trimmed nodes.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode:
while root and (root.val < low or root.val > high):
root = root.right if root.val < low else root.left
if not root:
return None
stack = []
current = root
while current or stack:
while current:
if low <= current.val <= high:
stack.append(current)
current = current.left
else:
current = current.right if current.val < low else current.left
if stack:
current = stack.pop().right
return rootimport java.util.Stack;
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int x) { val = x; }
}
public class Solution {
public TreeNode trimBST(TreeNode root, int low, int high) {
if (root == null) return null;
while (root != null && (root.val < low || root.val > high)) {
root = (root.val < low) ? root.right : root.left;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode current = root;
while (current != null || !stack.isEmpty()) {
while (current != null) {
if (low <= current.val && current.val <= high) {
stack.push(current);
current = current.left;
} else {
current = (current.val < low) ? current.right : current.left;
}
}
if (!stack.isEmpty()) {
current = stack.pop().right;
}
}
return root;
}
}#include <iostream>
#include <stack>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
TreeNode* trimBST(TreeNode* root, int low, int high) {
if (!root) return NULL;
while (root && (root->val < low || root->val > high)) {
root = root->val < low ? root->right : root->left;
}
TreeNode* current = root;
stack<TreeNode*> nodeStack;
while (current || !nodeStack.empty()) {
while (current) {
if (current->val >= low && current->val <= high) {
nodeStack.push(current);
current = current->left;
} else {
current = current->val < low ? current->right : current->left;
}
}
if (!nodeStack.empty()) {
current = nodeStack.top()->right;
nodeStack.pop();
}
}
return root;
}The Python solution employs a stack for an iterative node traversal. Adjustments are made according to the node values relative to low and high. Nodes are appended to a stack for traversal if they are valid.
Time Complexity: O(n)
Space Complexity: O(h)
Approach 3: Default Approach
/**
* Definition for a binary tree node.
* class TreeNode {
* val: number
* left: TreeNode | null
* right: TreeNode | null
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
* }
*/
function trimBST(root: TreeNode | null, low: number, high: number): TreeNode | null {
const dfs = (root: TreeNode | null) => {
if (root == null) {
return root;
}
const { val, left, right } = root;
if (val < low || val > high) {
return dfs(left) || dfs(right);
}
root.left = dfs(left);
root.right = dfs(right);
return root;
};
return dfs(root);
}/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func trimBST(root *TreeNode, low int, high int) *TreeNode {
if root == nil {
return root
}
if root.Val > high {
return trimBST(root.Left, low, high)
}
if root.Val < low {
return trimBST(root.Right, low, high)
}
root.Left = trimBST(root.Left, low, high)
root.Right = trimBST(root.Right, low, high)
return root
}// Definition for a binary tree node.
// #[derive(Debug, PartialEq, Eq)]
// pub struct TreeNode {
// pub val: i32,
// pub left: Option<Rc<RefCell<TreeNode>>>,
// pub right: Option<Rc<RefCell<TreeNode>>>,
// }
//
// impl TreeNode {
// #[inline]
// pub fn new(val: i32) -> Self {
// TreeNode {
// val,
// left: None,
// right: None
// }
// }
// }
use std::cell::RefCell;
use std::rc::Rc;
impl Solution {
pub fn trim_bst(
mut root: Option<Rc<RefCell<TreeNode>>>,
low: i32,
high: i32,
) -> Option<Rc<RefCell<TreeNode>>> {
if root.is_none() {
return root;
}
{
let mut node = root.as_mut().unwrap().borrow_mut();
if node.val < low {
return Self::trim_bst(node.right.take(), low, high);
}
if node.val > high {
return Self::trim_bst(node.left.take(), low, high);
}
node.left = Self::trim_bst(node.left.take(), low, high);
node.right = Self::trim_bst(node.right.take(), low, high);
}
root
}
}Complexity comparison
| Approach | Complexity |
|---|---|
| Recursive Trimming with BST Properties | Time Complexity: O(n) |
| Iterative Approach Using a Stack | Time Complexity: O(n) |
| Default Approach | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Recursive Trimming with BST Properties | O(n) | O(h) | Best general solution. Clean logic that leverages BST ordering to discard entire subtrees quickly. |
| Iterative DFS Using Stack | O(n) | O(h) | When recursion depth is a concern or the interviewer asks for an iterative tree traversal. |
Frequently asked questions
Is Trim a Binary Search Tree easy or hard?
Trim a Binary Search Tree is classified as a Medium problem. The implementation is short, but recognizing that the BST property lets you discard entire subtrees without scanning them individually requires solid understanding of tree traversal and BST ordering.
Trim a Binary Search Tree Python/Java solution
In Python or Java, the typical solution is a recursive function that returns the trimmed subtree root. The function checks whether the current node is outside the range and recursively processes left and right children when the node is valid. This approach runs in O(n) time and O(h) space.
How to solve Trim a Binary Search Tree in O(n)?
Traverse the tree using DFS and leverage the BST property. If a node value is below the allowed range, return its right subtree; if it is above the range, return its left subtree. Otherwise recursively trim both children and reconnect them. Each node is processed once, producing an O(n) time solution.
What is the best approach for Trim a Binary Search Tree?
The recursive trimming approach that uses BST ordering is the most efficient and commonly expected solution. If a node value is less than low, its entire left subtree can be skipped; if it is greater than high, its right subtree can be skipped. This allows the algorithm to trim the tree with a single depth‑first traversal in O(n) time and O(h) space.
Is Trim a Binary Search Tree asked at Google/Amazon/Meta?
Binary search tree manipulation problems appear frequently in interviews at companies like Amazon, Google, and Meta. Variations of tree pruning, subtree filtering, and BST property reasoning are common because they test recursion, DFS traversal, and understanding of tree invariants.
What data structure is used in Trim a Binary Search Tree?
The primary data structure is a binary search tree. The solution typically uses depth-first search traversal, either recursively or with an explicit stack, to examine nodes and remove subtrees that fall outside the allowed value range.
What is the time complexity of Trim a Binary Search Tree?
The time complexity is O(n) because every node in the tree is visited at most once during the trimming process. The space complexity is O(h), where h is the height of the tree, due to recursion stack or an explicit stack used for DFS traversal.
