Search in a BST
Imagine you have a large directory of sorted contacts stored as a binary search tree, and you want to quickly find if a particular contact exists.
Given the root node of a Binary Search Tree (BST) and a value val, determine if the BST contains a node with the value val. Return the subtree rooted with that node if it exists, otherwise return null.
The number of nodes in the tree is in the range [1, 10^5]Node values are unique integers-10^9 ≤ Node.val, val ≤ 10^9"root = [4,2,7,1,3], val = 2"[2,1,3]The node with value 2 is found, and its subtree includes nodes 1 and 3.
"root = [4,2,7,1,3], val = 5"nullNo node with value 5 exists in the BST.
- Searching for the root node value → should return entire tree
- Searching for a leaf node value → should return that leaf node
- Searching for a value smaller than all nodes → should return null
- Searching for a value larger than all nodes → should return null
Code runs slower, time complexity degrades to O(n)
✅ Use BST property to decide which subtree to search
Runtime errors or infinite recursion
✅ Always check if current node is null before accessing its value
Stack overflow error on large or skewed trees
✅ Use iterative approach for large inputs or mention recursion depth limits
Incorrect output, partial subtree or wrong node returned
✅ Return the node where value matches exactly, not just a partial match
Intuition
Traverse the entire tree recursively and check each node's value against the target. Return the node if found, else continue searching both left and right subtrees.
Algorithm
- Start at the root node.
- If the current node is null, return null (not found).
- If the current node's value equals the target, return the current node.
- Recursively search the left subtree; if found, return the result.
- Otherwise, recursively search the right subtree and return the result.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def searchBST(root, val):
if root is None:
return None
if root.val == val:
return root
left_search = searchBST(root.left, val)
if left_search is not None:
return left_search
return searchBST(root.right, val)
# Example usage:
# Construct BST: 4
# / \
# 2 7
# / \
# 1 3
root = TreeNode(4, TreeNode(2, TreeNode(1), TreeNode(3)), TreeNode(7))
result = searchBST(root, 2)
print(result.val if result else 'null')if root is None:Base case: if node is null, target not found hereif root.val == val:Check if current node matches targetleft_search = searchBST(root.left, val)Search left subtree recursivelyif left_search is not None:If found in left subtree, return immediatelyclass TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
public class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if (root == null) return null;
if (root.val == val) return root;
TreeNode leftSearch = searchBST(root.left, val);
if (leftSearch != null) return leftSearch;
return searchBST(root.right, val);
}
public static void main(String[] args) {
TreeNode root = new TreeNode(4);
root.left = new TreeNode(2);
root.right = new TreeNode(7);
root.left.left = new TreeNode(1);
root.left.right = new TreeNode(3);
Solution sol = new Solution();
TreeNode res = sol.searchBST(root, 2);
System.out.println(res != null ? res.val : "null");
}
}if (root == null) return null;Base case: no node means target not foundif (root.val == val) return root;Check current node for targetTreeNode leftSearch = searchBST(root.left, val);Search left subtree recursivelyif (leftSearch != null) return leftSearch;Return immediately if found in left subtree#include <iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if (!root) return nullptr;
if (root->val == val) return root;
TreeNode* leftSearch = searchBST(root->left, val);
if (leftSearch) return leftSearch;
return searchBST(root->right, val);
}
};
int main() {
TreeNode* root = new TreeNode(4);
root->left = new TreeNode(2);
root->right = new TreeNode(7);
root->left->left = new TreeNode(1);
root->left->right = new TreeNode(3);
Solution sol;
TreeNode* res = sol.searchBST(root, 2);
cout << (res ? to_string(res->val) : "null") << endl;
return 0;
}if (!root) return nullptr;Base case: no node means target not foundif (root->val == val) return root;Check current node for targetTreeNode* leftSearch = searchBST(root->left, val);Search left subtree recursivelyif (leftSearch) return leftSearch;Return immediately if found in left subtreeclass TreeNode {
constructor(val=0, left=null, right=null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function searchBST(root, val) {
if (root === null) return null;
if (root.val === val) return root;
const leftSearch = searchBST(root.left, val);
if (leftSearch !== null) return leftSearch;
return searchBST(root.right, val);
}
// Example usage:
const root = new TreeNode(4, new TreeNode(2, new TreeNode(1), new TreeNode(3)), new TreeNode(7));
const result = searchBST(root, 2);
console.log(result ? result.val : 'null');if (root === null) return null;Base case: no node means target not foundif (root.val === val) return root;Check current node for targetconst leftSearch = searchBST(root.left, val);Search left subtree recursivelyif (leftSearch !== null) return leftSearch;Return immediately if found in left subtreeO(n)O(h)In worst case, we visit every node (n nodes). The recursion stack can go as deep as the tree height h.
This approach works but ignores BST properties, so it is slower than necessary.
Intuition
At each node, compare the target value with the node's value. If equal, return node. If target is smaller, search left subtree only; if larger, search right subtree only.
Algorithm
- Start at the root node.
- If the current node is null, return null.
- If the current node's value equals the target, return the current node.
- If target < current node's value, recursively search the left subtree.
- Else, recursively search the right subtree.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def searchBST(root, val):
if root is None:
return None
if root.val == val:
return root
elif val < root.val:
return searchBST(root.left, val)
else:
return searchBST(root.right, val)
# Example usage:
root = TreeNode(4, TreeNode(2, TreeNode(1), TreeNode(3)), TreeNode(7))
result = searchBST(root, 2)
print(result.val if result else 'null')if root is None:Base case: no node means target not foundif root.val == val:Check if current node matches targetelif val < root.val:If target smaller, search left subtree onlyelse:If target larger, search right subtree onlyclass TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
public class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if (root == null) return null;
if (root.val == val) return root;
else if (val < root.val) return searchBST(root.left, val);
else return searchBST(root.right, val);
}
public static void main(String[] args) {
TreeNode root = new TreeNode(4);
root.left = new TreeNode(2);
root.right = new TreeNode(7);
root.left.left = new TreeNode(1);
root.left.right = new TreeNode(3);
Solution sol = new Solution();
TreeNode res = sol.searchBST(root, 2);
System.out.println(res != null ? res.val : "null");
}
}if (root == null) return null;Base case: no node means target not foundif (root.val == val) return root;Check current node for targetelse if (val < root.val) return searchBST(root.left, val);Search left subtree if target smallerelse return searchBST(root.right, val);Search right subtree if target larger#include <iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if (!root) return nullptr;
if (root->val == val) return root;
else if (val < root->val) return searchBST(root->left, val);
else return searchBST(root->right, val);
}
};
int main() {
TreeNode* root = new TreeNode(4);
root->left = new TreeNode(2);
root->right = new TreeNode(7);
root->left->left = new TreeNode(1);
root->left->right = new TreeNode(3);
Solution sol;
TreeNode* res = sol.searchBST(root, 2);
cout << (res ? to_string(res->val) : "null") << endl;
return 0;
}if (!root) return nullptr;Base case: no node means target not foundif (root->val == val) return root;Check current node for targetelse if (val < root->val) return searchBST(root->left, val);Search left subtree if target smallerelse return searchBST(root->right, val);Search right subtree if target largerclass TreeNode {
constructor(val=0, left=null, right=null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function searchBST(root, val) {
if (root === null) return null;
if (root.val === val) return root;
else if (val < root.val) return searchBST(root.left, val);
else return searchBST(root.right, val);
}
const root = new TreeNode(4, new TreeNode(2, new TreeNode(1), new TreeNode(3)), new TreeNode(7));
const result = searchBST(root, 2);
console.log(result ? result.val : 'null');if (root === null) return null;Base case: no node means target not foundif (root.val === val) return root;Check current node for targetelse if (val < root.val) return searchBST(root.left, val);Search left subtree if target smallerelse return searchBST(root.right, val);Search right subtree if target largerO(h)O(h)Each recursive call moves down one level of the tree, so time and space depend on tree height h, which is log n for balanced BSTs.
This approach leverages BST properties to reduce search time significantly.
Intuition
Start at root and iteratively move left or right depending on comparison with target until node is found or null is reached.
Algorithm
- Initialize current node as root.
- While current node is not null:
- If current node's value equals target, return current node.
- If target < current node's value, move to left child.
- Else, move to right child.
- If loop ends, target not found; return null.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def searchBST(root, val):
current = root
while current:
if current.val == val:
return current
elif val < current.val:
current = current.left
else:
current = current.right
return None
# Example usage:
root = TreeNode(4, TreeNode(2, TreeNode(1), TreeNode(3)), TreeNode(7))
result = searchBST(root, 2)
print(result.val if result else 'null')current = rootStart traversal from rootwhile current:Loop until we reach a null nodeif current.val == val:Check if current node matches targetcurrent = current.leftMove left if target smallerclass TreeNode {
int val;
TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
public class Solution {
public TreeNode searchBST(TreeNode root, int val) {
TreeNode current = root;
while (current != null) {
if (current.val == val) return current;
else if (val < current.val) current = current.left;
else current = current.right;
}
return null;
}
public static void main(String[] args) {
TreeNode root = new TreeNode(4);
root.left = new TreeNode(2);
root.right = new TreeNode(7);
root.left.left = new TreeNode(1);
root.left.right = new TreeNode(3);
Solution sol = new Solution();
TreeNode res = sol.searchBST(root, 2);
System.out.println(res != null ? res.val : "null");
}
}TreeNode current = root;Initialize traversal pointer at rootwhile (current != null) {Loop until no more nodesif (current.val == val) return current;Return node if foundelse if (val < current.val) current = current.left;Move left if target smaller#include <iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
TreeNode* current = root;
while (current) {
if (current->val == val) return current;
else if (val < current->val) current = current->left;
else current = current->right;
}
return nullptr;
}
};
int main() {
TreeNode* root = new TreeNode(4);
root->left = new TreeNode(2);
root->right = new TreeNode(7);
root->left->left = new TreeNode(1);
root->left->right = new TreeNode(3);
Solution sol;
TreeNode* res = sol.searchBST(root, 2);
cout << (res ? to_string(res->val) : "null") << endl;
return 0;
}TreeNode* current = root;Initialize traversal pointer at rootwhile (current) {Loop until no more nodesif (current->val == val) return current;Return node if foundelse if (val < current->val) current = current->left;Move left if target smallerclass TreeNode {
constructor(val=0, left=null, right=null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function searchBST(root, val) {
let current = root;
while (current !== null) {
if (current.val === val) return current;
else if (val < current.val) current = current.left;
else current = current.right;
}
return null;
}
const root = new TreeNode(4, new TreeNode(2, new TreeNode(1), new TreeNode(3)), new TreeNode(7));
const result = searchBST(root, 2);
console.log(result ? result.val : 'null');let current = root;Initialize traversal pointer at rootwhile (current !== null) {Loop until no more nodesif (current.val === val) return current;Return node if foundelse if (val < current.val) current = current.left;Move left if target smallerO(h)O(1)Iterative traversal visits nodes along one path from root to leaf, so time depends on height h. Space is constant as no recursion stack is used.
This is the preferred approach in interviews due to its efficiency and iterative style.
| Approach | Time | Space | Stack Risk | Reconstruct | Use In Interview |
|---|---|---|---|---|---|
| 1. Brute Force | O(n) | O(h) | Yes (deep recursion) | N/A | Mention only - never code |
| 2. Recursive BST Property Search | O(h) | O(h) | Yes (deep recursion) | N/A | Good for clarity and accepted in interviews |
| 3. Iterative BST Search | O(h) | O(1) | No | N/A | Optimal approach to code in interviews |
How to Present
Step 1: Clarify the problem and confirm input/output format.Step 2: Describe the brute force approach to show understanding.Step 3: Explain how BST properties allow pruning and implement recursive search.Step 4: Optimize to iterative solution to avoid recursion overhead.Step 5: Test with examples and edge cases.
Time Allocation
What the Interviewer Tests
Interviewer tests your understanding of BST properties, recursion vs iteration, and ability to optimize code for time and space.
Common Follow-ups
- What if the BST is unbalanced? → Time complexity degrades to O(n).
- Can you implement an iterative solution? → Yes, to save stack space.
When to Use
1) Input is a binary tree with BST property, 2) Need to find a node or value, 3) Target value given, 4) Return node or subtree
