Delete a Node in a Binary Search Tree
Problem and examples
Given a binary search tree (BST) and a value key, your task is to delete the node with the given key from the tree. If the node does not exist, return the original tree unchanged. The tree must remain a valid BST after the deletion.
A Binary Search Tree is a binary tree where each node has a value, and for any given node:
- The left subtree contains only nodes with values less than the node’s value.
- The right subtree contains only nodes with values greater than the node’s value.
You may assume that duplicate values do not exist in the BST.
| Input Tree | Key to Delete | Output Tree | Description |
|---|---|---|---|
| [5, 3, 6, 2, 4, null, 7] | 3 | [5, 4, 6, 2, null, null, 7] | Node 3 is deleted and replaced by its in-order successor (4). |
| [50, 30, 70, 20, 40, 60, 80] | 70 | [50, 30, 80, 20, 40, 60] | Node 70 is deleted and replaced by its in-order successor (80), maintaining BST properties. |
| [10, 5, 15, null, null, 12, 20] | 15 | [10, 5, 20, null, null, 12] | Node 15 is replaced by its in-order successor 20, which retains the subtree rooted at 12. |
| [8, 3, 10, 1, 6, null, 14] | 1 | [8, 3, 10, null, 6, null, 14] | Leaf node 1 is simply removed with no structural changes. |
| [7, 3, 9, null, 5, 8, 10] | 9 | [7, 3, 10, null, 5, 8] | Node 9 is replaced with its in-order successor 10. The subtree rooted at 8 remains intact. |
How it works
Understanding the Problem
We are given a Binary Search Tree (BST) and a key. Our goal is to delete the node with the given key from the BST while maintaining the properties of a BST:
- All values in the left subtree are less than the node’s value.
- All values in the right subtree are greater than the node’s value.
Step-by-Step Solution with Example
Step 1: Understand the Input Example
Consider the following BST:
5
/ 3 6
/ 2 4 7
Let’s say we want to delete the node with value 3. Node 3 has two children: 2 and 4.
Step 2: Traverse the Tree to Locate the Node
We start from the root (5). Since 3 is less than 5, we go to the left child. Now we are at 3, which matches the key. So this is the node we want to delete.
Step 3: Handle the Case - Node Has Two Children
Node 3 has both left and right children. In such cases, we:
- Find the in-order successor (smallest value in the right subtree). Here, that’s
4. - Replace the value of node 3 with 4.
- Now we recursively delete the node with value 4 in the right subtree.
Step 4: Recursively Delete the In-Order Successor
Now we move to the right subtree of node 3 (which now has value 4). We find node 4, which is a leaf (no children), so we delete it by returning null.
Step 5: Final Tree After Deletion
The updated BST looks like this:
5
/ 4 6
/ 2 7
Node 3 has been replaced by 4, and the original 4 node is removed.
Edge Cases
Edge Case 1: Deleting from an Empty Tree
If the root is null, the tree is empty. There's nothing to delete, so we return null.
Edge Case 2: Node Not Found
If we reach a null node during traversal and haven’t found the key, it means the key doesn’t exist. We simply return the current node.
Edge Case 3: Node is a Leaf
If the node to delete has no children, we can directly remove it by returning null.
Edge Case 4: Node with One Child
If the node has only one child, we return that child and skip the current node, effectively deleting it.
Finally
Deleting a node from a BST involves careful consideration of three scenarios: deleting a leaf, deleting a node with one child, and deleting a node with two children. Using recursion helps elegantly handle each case. Understanding in-order successor and BST structure rules is key to solving this problem correctly and efficiently.
- Given a binary search tree (BST) and a
keyto delete. - If the BST is empty, return
nullor the empty tree. - If
keyis less than the value at the current node, recursively delete the node in the left subtree. - If
keyis greater than the value at the current node, recursively delete the node in the right subtree. - If
keyequals the current node's value, then this is the node to be deleted: - If the node is a leaf, remove it.
- If the node has one child, replace it with its child.
- If the node has two children, find the minimum node in its right subtree (
successor), copy its value to the current node, and then recursively delete the successor from the right subtree. - Return the (possibly new) root of the BST.
Implementation and efficiency
C implementation
#include <stdio.h>
#include <stdlib.h>
typedef struct TreeNode {
int val;
struct TreeNode *left, *right;
} TreeNode;
TreeNode* createNode(int val) {
TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
node->val = val;
node->left = node->right = NULL;
return node;
}
TreeNode* findMin(TreeNode* node) {
while (node->left != NULL)
node = node->left;
return node;
}
TreeNode* deleteNode(TreeNode* root, int key) {
if (root == NULL) return NULL;
if (key < root->val)
root->left = deleteNode(root->left, key);
else if (key > root->val)
root->right = deleteNode(root->right, key);
else {
if (!root->left) {
TreeNode* temp = root->right;
free(root);
return temp;
} else if (!root->right) {
TreeNode* temp = root->left;
free(root);
return temp;
}
TreeNode* temp = findMin(root->right);
root->val = temp->val;
root->right = deleteNode(root->right, temp->val);
}
return root;
}
void inorder(TreeNode* root) {
if (root != NULL) {
inorder(root->left);
printf("%d ", root->val);
inorder(root->right);
}
}
int main() {
TreeNode* root = createNode(5);
root->left = createNode(3);
root->right = createNode(7);
root->left->left = createNode(2);
root->left->right = createNode(4);
root->right->left = createNode(6);
root->right->right = createNode(8);
root = deleteNode(root, 3);
printf("Inorder after deletion: ");
inorder(root);
printf("\n");
return 0;
}C++ implementation
#include <iostream>
using namespace std;
struct TreeNode {
int val;
TreeNode *left, *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
TreeNode* findMin(TreeNode* node) {
while (node->left != nullptr)
node = node->left;
return node;
}
TreeNode* deleteNode(TreeNode* root, int key) {
if (!root) return nullptr;
if (key < root->val) root->left = deleteNode(root->left, key);
else if (key > root->val) root->right = deleteNode(root->right, key);
else {
if (!root->left) {
TreeNode* temp = root->right;
delete root;
return temp;
} else if (!root->right) {
TreeNode* temp = root->left;
delete root;
return temp;
}
TreeNode* temp = findMin(root->right);
root->val = temp->val;
root->right = deleteNode(root->right, temp->val);
}
return root;
}
void inorder(TreeNode* root) {
if (root) {
inorder(root->left);
cout << root->val << " ";
inorder(root->right);
}
}
int main() {
TreeNode* root = new TreeNode(5);
root->left = new TreeNode(3);
root->right = new TreeNode(7);
root->left->left = new TreeNode(2);
root->left->right = new TreeNode(4);
root->right->left = new TreeNode(6);
root->right->right = new TreeNode(8);
root = deleteNode(root, 3);
cout << "Inorder after deletion: ";
inorder(root);
cout << endl;
return 0;
}Python implementation
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def findMin(node):
while node.left:
node = node.left
return node
def deleteNode(root, key):
if not root:
return None
if key < root.val:
root.left = deleteNode(root.left, key)
elif key > root.val:
root.right = deleteNode(root.right, key)
else:
if not root.left:
return root.right
elif not root.right:
return root.left
temp = findMin(root.right)
root.val = temp.val
root.right = deleteNode(root.right, temp.val)
return root
def inorder(root):
return inorder(root.left) + [root.val] + inorder(root.right) if root else []
if __name__ == '__main__':
root = TreeNode(5,
TreeNode(3, TreeNode(2), TreeNode(4)),
TreeNode(7, TreeNode(6), TreeNode(8)))
root = deleteNode(root, 3)
print("Inorder after deletion:", inorder(root))Java implementation
class TreeNode {
int val;
TreeNode left, right;
TreeNode(int x) { val = x; }
}
public class DeleteNodeInBST {
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) return null;
if (key < root.val)
root.left = deleteNode(root.left, key);
else if (key > root.val)
root.right = deleteNode(root.right, key);
else {
if (root.left == null) return root.right;
if (root.right == null) return root.left;
TreeNode min = findMin(root.right);
root.val = min.val;
root.right = deleteNode(root.right, min.val);
}
return root;
}
private TreeNode findMin(TreeNode node) {
while (node.left != null)
node = node.left;
return node;
}
private void inorder(TreeNode root) {
if (root != null) {
inorder(root.left);
System.out.print(root.val + " ");
inorder(root.right);
}
}
public static void main(String[] args) {
TreeNode root = new TreeNode(5);
root.left = new TreeNode(3);
root.right = new TreeNode(7);
root.left.left = new TreeNode(2);
root.left.right = new TreeNode(4);
root.right.left = new TreeNode(6);
root.right.right = new TreeNode(8);
DeleteNodeInBST obj = new DeleteNodeInBST();
root = obj.deleteNode(root, 3);
System.out.print("Inorder after deletion: ");
obj.inorder(root);
}
}JavaScript implementation
class TreeNode {
constructor(val, left = null, right = null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function findMin(node) {
while (node.left) node = node.left;
return node;
}
function deleteNode(root, key) {
if (!root) return null;
if (key < root.val)
root.left = deleteNode(root.left, key);
else if (key > root.val)
root.right = deleteNode(root.right, key);
else {
if (!root.left) return root.right;
if (!root.right) return root.left;
let temp = findMin(root.right);
root.val = temp.val;
root.right = deleteNode(root.right, temp.val);
}
return root;
}
function inorder(root) {
return root ? [...inorder(root.left), root.val, ...inorder(root.right)] : [];
}
const root = new TreeNode(5,
new TreeNode(3, new TreeNode(2), new TreeNode(4)),
new TreeNode(7, new TreeNode(6), new TreeNode(8)));
const updatedRoot = deleteNode(root, 3);
console.log("Inorder after deletion:", inorder(updatedRoot));Go implementation
package main
import (
"fmt"
)
type TreeNode struct {
val int
left *TreeNode
right *TreeNode
}
func findMin(node *TreeNode) *TreeNode {
for node.left != nil {
node = node.left
}
return node
}
func deleteNode(root *TreeNode, key int) *TreeNode {
if root == nil {
return nil
}
if key < root.val {
root.left = deleteNode(root.left, key)
} else if key > root.val {
root.right = deleteNode(root.right, key)
} else {
if root.left == nil {
return root.right
}
if root.right == nil {
return root.left
}
temp := findMin(root.right)
root.val = temp.val
root.right = deleteNode(root.right, temp.val)
}
return root
}
func inorder(root *TreeNode) {
if root != nil {
inorder(root.left)
fmt.Print(root.val, " ")
inorder(root.right)
}
}
func main() {
root := &TreeNode{5, nil, nil}
root.left = &TreeNode{3, &TreeNode{2, nil, nil}, &TreeNode{4, nil, nil}}
root.right = &TreeNode{7, &TreeNode{6, nil, nil}, &TreeNode{8, nil, nil}}
root = deleteNode(root, 3)
fmt.Print("Inorder after deletion: ")
inorder(root)
fmt.Println()
}Rust implementation
use std::rc::Rc;
use std::cell::RefCell;
type Node = Option<Rc<RefCell<TreeNode>>>;
#[derive(Debug)]
struct TreeNode {
val: i32,
left: Node,
right: Node,
}
fn find_min(mut node: Node) -> Node {
while let Some(left) = node.as_ref().unwrap().borrow().left.clone() {
node = Some(left);
}
node
}
fn delete_node(root: Node, key: i32) -> Node {
match root {
Some(node_rc) => {
let mut node = node_rc.borrow_mut();
if key < node.val {
node.left = delete_node(node.left.clone(), key);
} else if key > node.val {
node.right = delete_node(node.right.clone(), key);
} else {
if node.left.is_none() {
return node.right.clone();
}
if node.right.is_none() {
return node.left.clone();
}
let min_node = find_min(node.right.clone());
node.val = min_node.as_ref().unwrap().borrow().val;
node.right = delete_node(node.right.clone(), node.val);
}
Some(Rc::clone(&node_rc))
},
None => None,
}
}Kotlin implementation
class TreeNode(var val: Int) {
var left: TreeNode? = null
var right: TreeNode? = null
}
fun findMin(node: TreeNode): TreeNode {
var curr = node
while (curr.left != null) curr = curr.left!!
return curr
}
fun deleteNode(root: TreeNode?, key: Int): TreeNode? {
root ?: return null
if (key < root.val) {
root.left = deleteNode(root.left, key)
} else if (key > root.val) {
root.right = deleteNode(root.right, key)
} else {
if (root.left == null) return root.right
if (root.right == null) return root.left
val minNode = findMin(root.right!!)
root.val = minNode.val
root.right = deleteNode(root.right, minNode.val)
}
return root
}
fun inorder(root: TreeNode?) {
if (root != null) {
inorder(root.left)
print("${root.val} ")
inorder(root.right)
}
}
fun main() {
val root = TreeNode(5)
root.left = TreeNode(3).apply {
left = TreeNode(2)
right = TreeNode(4)
}
root.right = TreeNode(7).apply {
left = TreeNode(6)
right = TreeNode(8)
}
val updated = deleteNode(root, 3)
print("Inorder after deletion: ")
inorder(updated)
}Swift implementation
class TreeNode {
var val: Int
var left: TreeNode?
var right: TreeNode?
init(_ val: Int) {
self.val = val
self.left = nil
self.right = nil
}
}
func findMin(_ node: TreeNode) -> TreeNode {
var curr = node
while let next = curr.left {
curr = next
}
return curr
}
func deleteNode(_ root: TreeNode?, _ key: Int) -> TreeNode? {
guard let root = root else { return nil }
if key < root.val {
root.left = deleteNode(root.left, key)
} else if key > root.val {
root.right = deleteNode(root.right, key)
} else {
if root.left == nil { return root.right }
if root.right == nil { return root.left }
let minNode = findMin(root.right!)
root.val = minNode.val
root.right = deleteNode(root.right, minNode.val)
}
return root
}
func inorder(_ root: TreeNode?) {
if let node = root {
inorder(node.left)
print("\(node.val)", terminator: " ")
inorder(node.right)
}
}
let root = TreeNode(5)
root.left = TreeNode(3)
root.left?.left = TreeNode(2)
root.left?.right = TreeNode(4)
root.right = TreeNode(7)
root.right?.left = TreeNode(6)
root.right?.right = TreeNode(8)
let updated = deleteNode(root, 3)
print("Inorder after deletion:", terminator: " ")
inorder(updated)TypeScript implementation
class TreeNode {
val: number;
left: TreeNode | null;
right: TreeNode | null;
constructor(val: number, left: TreeNode | null = null, right: TreeNode | null = null) {
this.val = val;
this.left = left;
this.right = right;
}
}
function findMin(node: TreeNode): TreeNode {
while (node.left !== null) {
node = node.left;
}
return node;
}
function deleteNode(root: TreeNode | null, key: number): TreeNode | null {
if (root === null) return null;
if (key < root.val) {
root.left = deleteNode(root.left, key);
} else if (key > root.val) {
root.right = deleteNode(root.right, key);
} else {
if (!root.left) return root.right;
if (!root.right) return root.left;
let temp = findMin(root.right);
root.val = temp.val;
root.right = deleteNode(root.right, temp.val);
}
return root;
}
function inorder(root: TreeNode | null): number[] {
return root ? [...inorder(root.left), root.val, ...inorder(root.right)] : [];
}
const root = new TreeNode(5,
new TreeNode(3, new TreeNode(2), new TreeNode(4)),
new TreeNode(7, new TreeNode(6), new TreeNode(8)));
const updated = deleteNode(root, 3);
console.log("Inorder after deletion:", inorder(updated));