A Binary Search Tree (BST) is a specialized binary tree with an important ordering property: for every node, all values in the left subtree are less than the node’s value, and all values in the right subtree are greater. This simple property enables efficient searching, insertion, and deletion operations.
BSTs are one of the most important data structures in computer science, forming the basis for efficient ordered collections, database indexing, and many algorithmic solutions. By the end of this tutorial, you’ll understand how to implement, traverse, and use BSTs effectively.
Binary Search Trees provide O(log n) average-case performance for search, insert, and delete operations when balanced, making them significantly more efficient than linear data structures for ordered data.
What is a Binary Search Tree?
A BST is a binary tree with the BST property:
For every node N:
All values in the left subtree < N.value
All values in the right subtree > N.value
This property holds recursively for every node in the tree
Example of a valid BST:
8
/ \
3 10
/ \ \
1 6 14
/ \ /
4 7 13
NOT a BST (violates ordering):
8
/ \
3 10
/ \ \
1 12 14 ← 12 > 8, shouldn't be in left subtree
Why use a Binary Search Tree?
Choose a BST when:
You need ordered data with efficient operations
You need O(log n) search in average case
You need to find min/max, predecessor/successor efficiently
You need range queries (find all values between x and y)
You want dynamic data (unlike binary search on sorted array)
You need efficient insert/delete while maintaining order
Data is mostly sequential access (use linked list)
Real-world applications
BSTs are used extensively:
Databases: Index structures (though usually B-trees)
File systems: Organizing directory structures
Compilers: Symbol tables for variable lookup
Graphics: Spatial indexing for 2D/3D scenes
Autocomplete: Trie-based systems (specialized BST variant)
Priority queues: When both ordering and dynamic updates needed
Game development: Storing game objects by priority
Network routing: Routing table lookups
BST Implementation
Basic BST with insert and search
classTreeNode {
constructor(value) {
this.value=value;
this.left=null;
this.right=null;
}
}
classBinarySearchTree {
constructor() {
this.root=null;
}
// Insert a value
insert(value) {
constnewNode=newTreeNode(value);
if (this.root===null) {
this.root=newNode;
returnthis;
}
letcurrent=this.root;
while (true) {
if (value===current.value) returnundefined; // Duplicate
if (value<current.value) {
if (current.left===null) {
current.left=newNode;
returnthis;
}
current=current.left;
} else {
if (current.right===null) {
current.right=newNode;
returnthis;
}
current=current.right;
}
}
}
// Search for a value
search(value) {
letcurrent=this.root;
while (current!==null) {
if (value===current.value) returntrue;
if (value<current.value) {
current=current.left;
} else {
current=current.right;
}
}
returnfalse;
}
// Find node with value
find(value) {
letcurrent=this.root;
while (current!==null) {
if (value===current.value) returncurrent;
if (value<current.value) {
current=current.left;
} else {
current=current.right;
}
}
returnnull;
}
}
// Example usage
constbst=newBinarySearchTree();
bst.insert(8);
bst.insert(3);
bst.insert(10);
bst.insert(1);
bst.insert(6);
bst.insert(14);
console.log(bst.search(6)); // true
console.log(bst.search(12)); // false
classTreeNode:
def __init__(self, value):
self.value = value
self.left =None self.right =NoneclassBinarySearchTree:
def __init__(self):
self.root =Nonedefinsert(self, value):
"""Insert a value""" new_node = TreeNode(value)
if self.root isNone:
self.root = new_node
return self
current = self.root
whileTrue:
if value == current.value:
returnNone# Duplicateif value < current.value:
if current.left isNone:
current.left = new_node
return self
current = current.left
else:
if current.right isNone:
current.right = new_node
return self
current = current.right
defsearch(self, value):
"""Search for a value""" current = self.root
while current isnotNone:
if value == current.value:
returnTrueif value < current.value:
current = current.left
else:
current = current.right
returnFalsedeffind(self, value):
"""Find node with value""" current = self.root
while current isnotNone:
if value == current.value:
return current
if value < current.value:
current = current.left
else:
current = current.right
returnNone# Example usagebst = BinarySearchTree()
bst.insert(8)
bst.insert(3)
bst.insert(10)
bst.insert(1)
bst.insert(6)
bst.insert(14)
print(bst.search(6)) # Trueprint(bst.search(12)) # False
classTreeNode{int value; TreeNode left; TreeNode right; TreeNode(int value){this.value= value;this.left=null;this.right=null;}}publicclassBinarySearchTree{private TreeNode root;publicBinarySearchTree(){this.root=null;}// Insert a value
publicvoidinsert(int value){ TreeNode newNode =new TreeNode(value);if(root ==null){ root = newNode;return;} TreeNode current = root;while(true){if(value == current.value)return;// Duplicate
if(value < current.value){if(current.left==null){ current.left= newNode;return;} current = current.left;}else{if(current.right==null){ current.right= newNode;return;} current = current.right;}}}// Search for a value
publicbooleansearch(int value){ TreeNode current = root;while(current !=null){if(value == current.value)returntrue;if(value < current.value){ current = current.left;}else{ current = current.right;}}returnfalse;}// Find node with value
public TreeNode find(int value){ TreeNode current = root;while(current !=null){if(value == current.value)return current;if(value < current.value){ current = current.left;}else{ current = current.right;}}returnnull;}publicstaticvoidmain(String[] args){ BinarySearchTree bst =new BinarySearchTree(); bst.insert(8); bst.insert(3); bst.insert(10); bst.insert(1); bst.insert(6); bst.insert(14); System.out.println(bst.search(6));// true
System.out.println(bst.search(12));// false
}}
Common BST operations
Finding minimum and maximum
classBinarySearchTree {
// ... previous code
findMin(node=this.root) {
if (node===null) returnnull;
while (node.left!==null) {
node=node.left;
}
returnnode.value;
}
findMax(node=this.root) {
if (node===null) returnnull;
while (node.right!==null) {
node=node.right;
}
returnnode.value;
}
}
console.log(bst.findMin()); // 1
console.log(bst.findMax()); // 14
Deletion is the most complex operation in a BST. Three cases:
Leaf node (no children): Simply remove
One child: Replace node with its child
Two children: Replace with in-order successor (or predecessor), then delete successor
classBinarySearchTree {
// ... previous code
delete(value) {
this.root=this.deleteNode(this.root, value);
returnthis;
}
deleteNode(node, value) {
if (node===null) returnnull;
if (value<node.value) {
node.left=this.deleteNode(node.left, value);
returnnode;
} elseif (value>node.value) {
node.right=this.deleteNode(node.right, value);
returnnode;
} else {
// Found the node to delete
// Case 1: Leaf node
if (node.left===null&&node.right===null) {
returnnull;
}
// Case 2: One child
if (node.left===null) returnnode.right;
if (node.right===null) returnnode.left;
// Case 3: Two children
// Find in-order successor (min in right subtree)
letsuccessor=node.right;
while (successor.left!==null) {
successor=successor.left;
}
// Replace node's value with successor's value
node.value=successor.value;
// Delete the successor
node.right=this.deleteNode(node.right, successor.value);
returnnode;
}
}
}
bst.delete(3);
console.log(bst.search(3)); // false
classBinarySearchTree:
# ... previous codedefdelete(self, value):
self.root = self._delete_node(self.root, value)
return self
def_delete_node(self, node, value):
if node isNone:
returnNoneif value < node.value:
node.left = self._delete_node(node.left, value)
return node
elif value > node.value:
node.right = self._delete_node(node.right, value)
return node
else:
# Found the node to delete# Case 1: Leaf nodeif node.left isNoneand node.right isNone:
returnNone# Case 2: One childif node.left isNone:
return node.right
if node.right isNone:
return node.left
# Case 3: Two children# Find in-order successor (min in right subtree) successor = node.right
while successor.left isnotNone:
successor = successor.left
# Replace node's value with successor's value node.value = successor.value
# Delete the successor node.right = self._delete_node(node.right, successor.value)
return node
bst.delete(3)
print(bst.search(3)) # False
publicclassBinarySearchTree{// ... previous code
publicvoiddelete(int value){ root = deleteNode(root, value);}private TreeNode deleteNode(TreeNode node,int value){if(node ==null)returnnull;if(value < node.value){ node.left= deleteNode(node.left, value);return node;}elseif(value > node.value){ node.right= deleteNode(node.right, value);return node;}else{// Found the node to delete
// Case 1: Leaf node
if(node.left==null&& node.right==null){returnnull;}// Case 2: One child
if(node.left==null)return node.right;if(node.right==null)return node.left;// Case 3: Two children
// Find in-order successor (min in right subtree)
TreeNode successor = node.right;while(successor.left!=null){ successor = successor.left;}// Replace node's value with successor's value
node.value= successor.value;// Delete the successor
node.right= deleteNode(node.right, successor.value);return node;}}}bst.delete(3);System.out.println(bst.search(3));// false
Validating a BST
Check if a binary tree satisfies the BST property:
functionisValidBST(node, min=-Infinity, max=Infinity) {
if (node===null) returntrue;
// Current node must be within the valid range
if (node.value<=min||node.value>=max) {
returnfalse;
}
// Recursively check left and right subtrees
returnisValidBST(node.left, min, node.value) &&isValidBST(node.right, node.value, max);
}
console.log(isValidBST(bst.root)); // true
defis_valid_bst(node, min_val=float('-inf'), max_val=float('inf')):
if node isNone:
returnTrue# Current node must be within the valid rangeif node.value <= min_val or node.value >= max_val:
returnFalse# Recursively check left and right subtreesreturn (is_valid_bst(node.left, min_val, node.value) and is_valid_bst(node.right, node.value, max_val))
print(is_valid_bst(bst.root)) # True
publicstaticbooleanisValidBST(TreeNode node){return isValidBSTHelper(node, Long.MIN_VALUE, Long.MAX_VALUE);}privatestaticbooleanisValidBSTHelper(TreeNode node,long min,long max){if(node ==null)returntrue;// Current node must be within the valid range
if(node.value<= min || node.value>= max){returnfalse;}// Recursively check left and right subtrees
return isValidBSTHelper(node.left, min, node.value)&& isValidBSTHelper(node.right, node.value, max);}System.out.println(isValidBST(bst.root));// true
Recursion stack: O(h) where h is height (O(log n) balanced, O(n) skewed)
Binary Search Trees combine the benefits of binary search with dynamic insertion and deletion. While basic BSTs can degrade to O(n) operations, they form the foundation for self-balancing variants (AVL, Red-Black) that guarantee O(log n) performance.
Thanks for visiting
We are actively updating content to this site. Thanks for visiting! Please bookmark this page and visit again soon.