Binary Search Tree
Data Structure · Core
Step through it: Binary Tree Traversal pattern
Step 1 of 5
A tree has no single first-to-last order, so we pick one. The three common orders differ only in when a node is visited compared with its children.
Step 2 of 5
Pre-order: the node first, then left, then right. Good for copying a tree.
Step 3 of 5
In-order: left, then the node, then right. On a binary search tree this gives the values in sorted order.
Step 4 of 5
Post-order: left, right, then the node. Children come before their parent, so it is the order for deleting a tree or adding up sizes.
Step 5 of 5
Your turn.
Complexity
Time Complexity
| Access | Search | Insertion | Deletion |
|---|---|---|---|
| O(log(n)) | O(log(n)) | O(log(n)) | O(log(n)) |
Space Complexity
O(n)
In computer science, binary search trees (BST), sometimes called ordered or sorted binary trees, are a particular type of container: data structures that store "items" (such as numbers, names etc.) in memory. They allow fast lookup, addition and removal of items, and can be used to implement either dynamic sets of items, or lookup tables that allow finding an item by its key (e.g., finding the phone number of a person by name).
Binary search trees keep their keys in sorted order, so that lookup and other operations can use the principle of binary search: when looking for a key in a tree (or a place to insert a new key), they traverse the tree from root to leaf, making comparisons to keys stored in the nodes of the tree and deciding, on the basis of the comparison, to continue searching in the left or right subtrees. On average, this means that each comparison allows the operations to skip about half of the tree, so that each lookup, insertion or deletion takes time proportional to the logarithm of the number of items stored in the tree. This is much better than the linear time required to find items by key in an (unsorted) array, but slower than the corresponding operations on hash tables.
A binary search tree of size 9 and depth 3, with 8 at the root. The leaves are not drawn.

Made with okso.app
Pseudocode for Basic Operations
Insertion
insert(value)
Pre: value has passed custom type checks for type T
Post: value has been placed in the correct location in the tree
if root = ø
root ← node(value)
else
insertNode(root, value)
end if
end insertinsertNode(current, value)
Pre: current is the node to start from
Post: value has been placed in the correct location in the tree
if value < current.value
if current.left = ø
current.left ← node(value)
else
InsertNode(current.left, value)
end if
else
if current.right = ø
current.right ← node(value)
else
InsertNode(current.right, value)
end if
end if
end insertNodeSearching
contains(root, value)
Pre: root is the root node of the tree, value is what we would like to locate
Post: value is either located or not
if root = ø
return false
end if
if root.value = value
return true
else if value < root.value
return contains(root.left, value)
else
return contains(root.right, value)
end if
end containsDeletion
remove(value)
Pre: value is the value of the node to remove, root is the node of the BST
count is the number of items in the BST
Post: node with value is removed if found in which case yields true, otherwise false
nodeToRemove ← findNode(value)
if nodeToRemove = ø
return false
end if
parent ← findParent(value)
if count = 1
root ← ø
else if nodeToRemove.left = ø and nodeToRemove.right = ø
if nodeToRemove.value < parent.value
parent.left ← nodeToRemove.right
else
parent.right ← nodeToRemove.right
end if
else if nodeToRemove.left != ø and nodeToRemove.right != ø
next ← nodeToRemove.right
while next.left != ø
next ← next.left
end while
if next != nodeToRemove.right
remove(next.value)
nodeToRemove.value ← next.value
else
nodeToRemove.value ← next.value
nodeToRemove.right ← nodeToRemove.right.right
end if
else
if nodeToRemove.left = ø
next ← nodeToRemove.right
else
next ← nodeToRemove.left
end if
if root = nodeToRemove
root = next
else if parent.left = nodeToRemove
parent.left = next
else if parent.right = nodeToRemove
parent.right = next
end if
end if
count ← count - 1
return true
end removeFind Parent of Node
findParent(value, root)
Pre: value is the value of the node we want to find the parent of
root is the root node of the BST and is != ø
Post: a reference to the prent node of value if found; otherwise ø
if value = root.value
return ø
end if
if value < root.value
if root.left = ø
return ø
else if root.left.value = value
return root
else
return findParent(value, root.left)
end if
else
if root.right = ø
return ø
else if root.right.value = value
return root
else
return findParent(value, root.right)
end if
end if
end findParentFind Node
findNode(root, value)
Pre: value is the value of the node we want to find the parent of
root is the root node of the BST
Post: a reference to the node of value if found; otherwise ø
if root = ø
return ø
end if
if root.value = value
return root
else if value < root.value
return findNode(root.left, value)
else
return findNode(root.right, value)
end if
end findNodeFind Minimum
findMin(root)
Pre: root is the root node of the BST
root = ø
Post: the smallest value in the BST is located
if root.left = ø
return root.value
end if
findMin(root.left)
end findMinFind Maximum
findMax(root)
Pre: root is the root node of the BST
root = ø
Post: the largest value in the BST is located
if root.right = ø
return root.value
end if
findMax(root.right)
end findMaxTraversal
InOrder Traversal
inorder(root)
Pre: root is the root node of the BST
Post: the nodes in the BST have been visited in inorder
if root != ø
inorder(root.left)
yield root.value
inorder(root.right)
end if
end inorderPreOrder Traversal
preorder(root)
Pre: root is the root node of the BST
Post: the nodes in the BST have been visited in preorder
if root != ø
yield root.value
preorder(root.left)
preorder(root.right)
end if
end preorderPostOrder Traversal
postorder(root)
Pre: root is the root node of the BST
Post: the nodes in the BST have been visited in postorder
if root != ø
postorder(root.left)
postorder(root.right)
yield root.value
end if
end postorderCode
import BinarySearchTreeNode from './BinarySearchTreeNode';
export default class BinarySearchTree {
/**
* @param {function} [nodeValueCompareFunction]
*/
constructor(nodeValueCompareFunction) {
this.root = new BinarySearchTreeNode(null, nodeValueCompareFunction);
// Steal node comparator from the root.
this.nodeComparator = this.root.nodeComparator;
}
/**
* @param {*} value
* @return {BinarySearchTreeNode}
*/
insert(value) {
return this.root.insert(value);
}
/**
* @param {*} value
* @return {boolean}
*/
contains(value) {
return this.root.contains(value);
}
/**
* @param {*} value
* @return {boolean}
*/
remove(value) {
return this.root.remove(value);
}
/**
* @return {string}
*/
toString() {
return this.root.toString();
}
}import BinaryTreeNode from '../BinaryTreeNode';
import Comparator from '../../../utils/comparator/Comparator';
export default class BinarySearchTreeNode extends BinaryTreeNode {
/**
* @param {*} [value] - node value.
* @param {function} [compareFunction] - comparator function for node values.
*/
constructor(value = null, compareFunction = undefined) {
super(value);
// This comparator is used to compare node values with each other.
this.compareFunction = compareFunction;
this.nodeValueComparator = new Comparator(compareFunction);
}
/**
* @param {*} value
* @return {BinarySearchTreeNode}
*/
insert(value) {
if (this.nodeValueComparator.equal(this.value, null)) {
this.value = value;
return this;
}
if (this.nodeValueComparator.lessThan(value, this.value)) {
// Insert to the left.
if (this.left) {
return this.left.insert(value);
}
const newNode = new BinarySearchTreeNode(value, this.compareFunction);
this.setLeft(newNode);
return newNode;
}
if (this.nodeValueComparator.greaterThan(value, this.value)) {
// Insert to the right.
if (this.right) {
return this.right.insert(value);
}
const newNode = new BinarySearchTreeNode(value, this.compareFunction);
this.setRight(newNode);
return newNode;
}
return this;
}
/**
* @param {*} value
* @return {BinarySearchTreeNode}
*/
find(value) {
// Check the root.
if (this.nodeValueComparator.equal(this.value, value)) {
return this;
}
if (this.nodeValueComparator.lessThan(value, this.value) && this.left) {
// Check left nodes.
return this.left.find(value);
}
if (this.nodeValueComparator.greaterThan(value, this.value) && this.right) {
// Check right nodes.
return this.right.find(value);
}
return null;
}
/**
* @param {*} value
* @return {boolean}
*/
contains(value) {
return !!this.find(value);
}
/**
* @param {*} value
* @return {boolean}
*/
remove(value) {
const nodeToRemove = this.find(value);
if (!nodeToRemove) {
throw new Error('Item not found in the tree');
}
const { parent } = nodeToRemove;
if (!nodeToRemove.left && !nodeToRemove.right) {
// Node is a leaf and thus has no children.
if (parent) {
// Node has a parent. Just remove the pointer to this node from the parent.
parent.removeChild(nodeToRemove);
} else {
// Node has no parent. Just erase current node value.
nodeToRemove.setValue(undefined);
}
} else if (nodeToRemove.left && nodeToRemove.right) {
// Node has two children.
// Find the next biggest value (minimum value in the right branch)
// and replace current value node with that next biggest value.
const nextBiggerNode = nodeToRemove.right.findMin();
if (!this.nodeComparator.equal(nextBiggerNode, nodeToRemove.right)) {
this.remove(nextBiggerNode.value);
nodeToRemove.setValue(nextBiggerNode.value);
} else {
// In case if next right value is the next bigger one and it doesn't have left child
// then just replace node that is going to be deleted with the right node.
nodeToRemove.setValue(nodeToRemove.right.value);
nodeToRemove.setRight(nodeToRemove.right.right);
}
} else {
// Node has only one child.
// Make this child to be a direct child of current node's parent.
/** @var BinarySearchTreeNode */
const childNode = nodeToRemove.left || nodeToRemove.right;
if (parent) {
parent.replaceChild(nodeToRemove, childNode);
} else {
BinaryTreeNode.copyNode(childNode, nodeToRemove);
}
}
// Clear the parent of removed node.
nodeToRemove.parent = null;
return true;
}
/**
* @return {BinarySearchTreeNode}
*/
findMin() {
if (!this.left) {
return this;
}
return this.left.findMin();
}
}References
Practice
Binary Tree Traversal
- Binary Tree Inorder TraversalEasy
- Binary Tree Preorder TraversalEasy
- Binary Tree Postorder TraversalEasy
- Same TreeEasy
- Symmetric TreeEasy
- Invert Binary TreeEasy
- Subtree of Another TreeEasy
- Merge Two Binary TreesEasy
- Construct Binary Tree from Preorder and Inorder TraversalMedium
- Construct Binary Tree from Inorder and Postorder TraversalMedium
- Validate Binary Search TreeMedium
- Kth Smallest Element in a BSTMedium
- Flatten Binary Tree to Linked ListMedium
- Lowest Common Ancestor of a Binary TreeMedium
- Binary Search Tree IteratorMedium
- Recover Binary Search TreeMedium
- Serialize and Deserialize Binary TreeHard
- Binary Tree Maximum Path SumHard
Spotted a mistake or something unclear? Report it on GitHub.