Skip to main content

Binary Search Tree (BST)

Basic Structure

class Node {
constructor(data) {
this.data = data;
this.left = null;
this.right = null;
}
}

class BinarySearchTree {
constructor() {
this.root = null;
}
}

BST Core Operations

1. Insert Node

insert(data) {
const newNode = new Node(data);

if (!this.root) {
this.root = newNode;
return;
}

function insertNode(node, newNode) {
if (newNode.data < node.data) {
if (!node.left) {
node.left = newNode;
} else {
insertNode(node.left, newNode);
}
} else {
if (!node.right) {
node.right = newNode;
} else {
insertNode(node.right, newNode);
}
}
}

insertNode(this.root, newNode);
}

Time Complexity: O(log n) average, O(n) worst case

2. Search Node

search(data) {
function searchNode(node, data) {
if (!node || node.data === data) {
return node;
}

if (data < node.data) {
return searchNode(node.left, data);
}
return searchNode(node.right, data);
}

return searchNode(this.root, data);
}

Time Complexity: O(log n) average, O(n) worst case

3. Delete Node

delete(data) {
function findMin(node) {
while (node.left) {
node = node.left;
}
return node;
}

function deleteNode(node, data) {
if (!node) return null;

if (data < node.data) {
node.left = deleteNode(node.left, data);
} else if (data > node.data) {
node.right = deleteNode(node.right, data);
} else {
// Node with only one child or no child
if (!node.left) return node.right;
if (!node.right) return node.left;

// Node with two children
const temp = findMin(node.right);
node.data = temp.data;
node.right = deleteNode(node.right, temp.data);
}
return node;
}

this.root = deleteNode(this.root, data);
}

Time Complexity: O(log n) average, O(n) worst case

Tree Traversal Methods

1. Inorder Traversal (Left-Root-Right)

inorderTraversal() {
const result = [];

function inorder(node) {
if (node) {
inorder(node.left);
result.push(node.data);
inorder(node.right);
}
}

inorder(this.root);
return result;
}

2. Preorder Traversal (Root-Left-Right)

preorderTraversal() {
const result = [];

function preorder(node) {
if (node) {
result.push(node.data);
preorder(node.left);
preorder(node.right);
}
}

preorder(this.root);
return result;
}

3. Postorder Traversal (Left-Right-Root)

postorderTraversal() {
const result = [];

function postorder(node) {
if (node) {
postorder(node.left);
postorder(node.right);
result.push(node.data);
}
}

postorder(this.root);
return result;
}

4. Level Order Traversal (BFS)

levelOrderTraversal() {
if (!this.root) return [];

const result = [];
const queue = [this.root];

while (queue.length) {
const level = [];
const levelSize = queue.length;

for (let i = 0; i < levelSize; i++) {
const node = queue.shift();
level.push(node.data);

if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}

result.push(level);
}

return result;
}

Common Tree Operations

1. Find Height

getHeight() {
function height(node) {
if (!node) return 0;
return 1 + Math.max(height(node.left), height(node.right));
}

return height(this.root);
}

2. Check if BST is Valid

isValidBST() {
function validate(node, min, max) {
if (!node) return true;

if (node.data <= min || node.data >= max) {
return false;
}

return validate(node.left, min, node.data) &&
validate(node.right, node.data, max);
}

return validate(this.root, -Infinity, Infinity);
}

3. Find Lowest Common Ancestor

findLCA(n1, n2) {
function findLCANode(node, n1, n2) {
if (!node) return null;

if (node.data > n1 && node.data > n2) {
return findLCANode(node.left, n1, n2);
}

if (node.data < n1 && node.data < n2) {
return findLCANode(node.right, n1, n2);
}

return node;
}

return findLCANode(this.root, n1, n2);
}

4. Check if Tree is Balanced

isBalanced() {
function checkBalance(node) {
if (!node) return 0;

const leftHeight = checkBalance(node.left);
if (leftHeight === -1) return -1;

const rightHeight = checkBalance(node.right);
if (rightHeight === -1) return -1;

if (Math.abs(leftHeight - rightHeight) > 1) return -1;

return 1 + Math.max(leftHeight, rightHeight);
}

return checkBalance(this.root) !== -1;
}

Common Interview Questions

1. Serialize and Deserialize Binary Tree

// Serialize
serialize() {
if (!this.root) return '[]';

const result = [];
const queue = [this.root];

while (queue.length) {
const node = queue.shift();
if (node) {
result.push(node.data);
queue.push(node.left);
queue.push(node.right);
} else {
result.push(null);
}
}

while (result[result.length - 1] === null) {
result.pop();
}

return JSON.stringify(result);
}

// Deserialize
static deserialize(data) {
const values = JSON.parse(data);
if (!values.length) return null;

const tree = new BinarySearchTree();
tree.root = new Node(values[0]);
const queue = [tree.root];
let i = 1;

while (queue.length && i < values.length) {
const node = queue.shift();

if (i < values.length && values[i] !== null) {
node.left = new Node(values[i]);
queue.push(node.left);
}
i++;

if (i < values.length && values[i] !== null) {
node.right = new Node(values[i]);
queue.push(node.right);
}
i++;
}

return tree;
}

Time Complexity Summary

OperationAverageWorst Case
InsertO(log n)O(n)
DeleteO(log n)O(n)
SearchO(log n)O(n)
Traversal (any)O(n)O(n)
HeightO(n)O(n)
Validate BSTO(n)O(n)
Find LCAO(log n)O(n)
Check BalanceO(n)O(n)

Usage Example

const bst = new BinarySearchTree();

// Insert nodes
bst.insert(8);
bst.insert(3);
bst.insert(10);
bst.insert(1);
bst.insert(6);

// Traverse
console.log(bst.inorderTraversal()); // [1, 3, 6, 8, 10]
console.log(bst.preorderTraversal()); // [8, 3, 1, 6, 10]
console.log(bst.postorderTraversal()); // [1, 6, 3, 10, 8]

// Search
console.log(bst.search(6)); // Node { data: 6, left: null, right: null }

// Check if valid BST
console.log(bst.isValidBST()); // true

// Get height
console.log(bst.getHeight()); // 3