Given the root node of a binary search tree (BST) and a value key.
Return the root node of the BST after the deletion of the node with the given key value.
Input : root = [5, 3, 6, 2, 4, null, 7] , key = 3
Output : [5, 4, 6, 2, null, null, 7]
Explanation : Below is image of the original BST
Below is image where the node 3 is deleted
Input : root = [5, 3, 6, 2, 4, null, 7] , key = 0
Output : [5, 3, 6, 2, 4, null, 7]
Explanation : The tree does not have node with value 0.
Input : root = [5, 3, 6, 2, 4, null, 7] , key = 5
Deleting a node in a Binary Search Tree (BST) requires addressing various scenarios based on the node's children. If the node has no children, it can be simply removed. If the node has one child, it is replaced by its single child. If the node has two children, the challenge is to maintain the BST properties. The node must be replaced by its in-order successor (the smallest node in the right subtree), which ensures the structure remains valid. The key task is to adjust the pointers of the parent node to reflect these changes, ensuring the integrity of the BST.
connector
, to manage the replacement of a node with one or two children.connector
function:
deleteNode
function:
None
.connector
function to handle the replacement.connector
function to the found node to complete its deletion and return the modified tree.#include <bits/stdc++.h>
using namespace std;
// Definition for a binary tree node.
struct TreeNode {
int data;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : data(x), left(nullptr), right(nullptr) {}
};
class Solution {
public:
// Helper function to connect subtrees after deleting a node
TreeNode* connector(TreeNode* root) {
// Case 1: If the node has no left child,
// return the right subtree to bypass the node.
if (!root->left)
return root->right;
// Case 2: If the node has no right child,
// return the left subtree to bypass the node.
else if (!root->right)
return root->left;
/*
Case 3: If the node has both left and right children:
1. Save the left subtree in a temporary variable.
2. Find the leftmost node in the right subtree.
3. Attach the left subtree to the leftmost node in the right subtree. */
TreeNode* leftchild = root->left;
TreeNode* leftmost_child_in_right_subtree = root->right;
// Traverse to the leftmost node in the right subtree.
while (leftmost_child_in_right_subtree->left) {
leftmost_child_in_right_subtree =
leftmost_child_in_right_subtree->left;
}
// Attach the left subtree to the leftmost node in the right subtree.
leftmost_child_in_right_subtree->left = leftchild;
// Return the right subtree as the new root of the modified tree.
return root->right;
}
// Function to delete a node with a specific key from the binary search tree
TreeNode* deleteNode(TreeNode* root, int key) {
// Base case: if the tree is empty, return NULL.
if (root == NULL)
return NULL;
// If the node to be deleted is the root node,
// use the connector function to replace the root.
if (root->data == key) {
return connector(root);
}
// Traverse the tree to find the node to be deleted.
TreeNode* node = root;
while (node) {
// If the key to be deleted is smaller than the current node's data,
// move to the left subtree.
if (node->data > key) {
// If the left child is the node to be deleted,
// update the left child with the connector function.
if (node->left && node->left->data == key) {
node->left = connector(node->left);
break;
} else {
node = node->left;
}
}
// If the key to be deleted is larger than the current node's data,
// move to the right subtree.
else {
// If the right child is the node to be deleted,
// update the right child with the connector function.
if (node->right && node->right->data == key) {
node->right = connector(node->right);
break;
} else {
node = node->right;
}
}
}
// Return the modified tree with the node deleted.
return root;
}
};
int main() {
// Create a sample binary search tree.
TreeNode* root = new TreeNode(5);
root->left = new TreeNode(3);
root->right = new TreeNode(8);
root->left->left = new TreeNode(2);
root->left->right = new TreeNode(4);
Solution sol;
// Delete node with key 3 from the tree.
root = sol.deleteNode(root, 3);
return 0;
}
// Definition for a binary tree node.
public class TreeNode {
int data;
TreeNode left;
TreeNode right;
TreeNode(int val) { data = val; left = null, right = null }
}
class Solution {
// Helper function to connect subtrees after deleting a node
private TreeNode connector(TreeNode root) {
// Case 1: If the node has no left child,
// return the right subtree to bypass the node.
if (root.left == null) return root.right;
// Case 2: If the node has no right child,
// return the left subtree to bypass the node.
if (root.right == null) return root.left;
/*
Case 3: If the node has both left and right children:
1. Save the left subtree in a temporary variable.
2. Find the leftmost node in the right subtree.
3. Attach the left subtree to the leftmost node in the right subtree. */
TreeNode leftChild = root.left;
TreeNode leftmostChildInRightSubtree = root.right;
// Traverse to the leftmost node in the right subtree.
while (leftmostChildInRightSubtree.left != null) {
leftmostChildInRightSubtree = leftmostChildInRightSubtree.left;
}
// Attach the left subtree to the leftmost node in the right subtree.
leftmostChildInRightSubtree.left = leftChild;
// Return the right subtree as the new root of the modified tree.
return root.right;
}
// Function to delete a node with a specific key from the binary search tree
public TreeNode deleteNode(TreeNode root, int key) {
// Base case: if the tree is empty, return null.
if (root == null) return null;
// If the node to be deleted is the root node,
// use the connector function to replace the root.
if (root.data == key) {
return connector(root);
}
// Traverse the tree to find the node to be deleted.
TreeNode node = root;
while (node != null) {
// If the key to be deleted is smaller than the current node's data,
// move to the left subtree.
if (node.data > key) {
// If the left child is the node to be deleted,
// update the left child with the connector function.
if (node.left != null && node.left.data == key) {
node.left = connector(node.left);
break;
} else {
node = node.left;
}
}
// If the key to be deleted is larger than the current node's data,
// move to the right subtree.
else {
// If the right child is the node to be deleted,
// update the right child with the connector function.
if (node.right != null && node.right.data == key) {
node.right = connector(node.right);
break;
} else {
node = node.right;
}
}
}
// Return the modified tree with the node deleted.
return root;
}
public static void main(String[] args) {
// Create a sample binary search tree.
TreeNode root = new TreeNode(5);
root.left = new TreeNode(3);
root.right = new TreeNode(8);
root.left.left = new TreeNode(2);
root.left.right = new TreeNode(4);
Solution sol = new Solution();
// Delete node with key 3 from the tree.
root = sol.deleteNode(root, 3);
}
}
class TreeNode:
def __init__(self, x):
self.data = x
self.left = None
self.right = None
class Solution:
# Helper function to connect subtrees after deleting a node
def connector(self, root):
# If the node has no left child, return the right subtree.
# This effectively removes the node by bypassing it with its right child.
if not root.left:
return root.right
# If the node has no right child, return the left subtree.
# This effectively removes the node by bypassing it with its left child.
elif not root.right:
return root.left
# If the node has both left and right children, handle the case as follows:
# 1. Save the left subtree in a temporary variable.
# 2. Find the leftmost node in the right subtree.
# 3. Attach the left subtree to the leftmost node in the right subtree.
leftchild = root.left
leftmost_child_in_right_subtree = root.right
# Traverse to the leftmost node in the right subtree.
while leftmost_child_in_right_subtree.left:
leftmost_child_in_right_subtree = leftmost_child_in_right_subtree.left
# Attach the left subtree to the leftmost node in the right subtree.
leftmost_child_in_right_subtree.left = leftchild
# Return the right subtree as the new root of the modified tree.
return root.right
# Function to delete a node with a specific key from the binary search tree.
def deleteNode(self, root, key):
# If the tree is empty, there is nothing to delete.
if root is None:
return None
# If the node to be deleted is the root node, use the connector function.
if root.data == key:
return self.connector(root)
# Traverse the tree to find the node to be deleted.
node = root
while node:
# If the key to be deleted is smaller than the current node's data,
# move to the left subtree.
if node.data > key:
# If the left child is the node to be deleted, update the left child.
if node.left and node.left.data == key:
node.left = self.connector(node.left)
break
else:
node = node.left
# If the key to be deleted is larger than the current node's data,
# move to the right subtree.
else:
# If the right child is the node to be deleted, update the right child.
if node.right and node.right.data == key:
node.right = self.connector(node.right)
break
else:
node = node.right
# Return the modified tree with the node deleted.
return root
# Example usage
if __name__ == "__main__":
# Create a sample binary search tree
root = TreeNode(5)
root.left = TreeNode(3)
root.right = TreeNode(8)
root.left.left = TreeNode(2)
root.left.right = TreeNode(4)
sol = Solution()
# Delete node with key 3 from the tree
root = sol.deleteNode(root, 3)
class TreeNode {
constructor(x) {
this.data = x;
this.left = null;
this.right = null;
}
}
class Solution {
// Helper function to connect subtrees after deleting a node
connector(root) {
// Case 1: If the node has no left child,
// return the right subtree to bypass the node.
if (!root.left) return root.right;
// Case 2: If the node has no right child,
// return the left subtree to bypass the node.
else if (!root.right) return root.left;
/*
Case 3: If the node has both left and right children:
1. Save the left subtree in a temporary variable.
2. Find the leftmost node in the right subtree.
3. Attach the left subtree to the leftmost node in the right subtree.
*/
let leftChild = root.left;
let leftmostChildInRightSubtree = root.right;
// Traverse to the leftmost node in the right subtree.
while (leftmostChildInRightSubtree.left) {
leftmostChildInRightSubtree = leftmostChildInRightSubtree.left;
}
// Attach the left subtree to the leftmost node in the right subtree.
leftmostChildInRightSubtree.left = leftChild;
// Return the right subtree as the new root of the modified tree.
return root.right;
}
// Function to delete a node with a specific key from the binary search tree
deleteNode(root, key) {
// Base case: if the tree is empty, return null.
if (root === null) return null;
// If the node to be deleted is the root node,
// use the connector function to replace the root.
if (root.data === key) {
return this.connector(root);
}
// Traverse the tree to find the node to be deleted.
let node = root;
while (node) {
// If the key to be deleted is smaller than the current node's data,
// move to the left subtree.
if (node.data > key) {
// If the left child is the node to be deleted,
// update the left child with the connector function.
if (node.left && node.left.data === key) {
node.left = this.connector(node.left);
break;
} else {
node = node.left;
}
}
// If the key to be deleted is larger than the current node's data,
// move to the right subtree.
else {
// If the right child is the node to be deleted,
// update the right child with the connector function.
if (node.right && node.right.data === key) {
node.right = this.connector(node.right);
break;
} else {
node = node.right;
}
}
}
// Return the modified tree with the node deleted.
return root;
}
}
// Example usage
function main() {
// Create a sample binary search tree.
let root = new TreeNode(5);
root.left = new TreeNode(3);
root.right = new TreeNode(8);
root.left.left = new TreeNode(2);
root.left.right = new TreeNode(4);
let sol = new Solution();
// Delete node with key 3 from the tree.
root = sol.deleteNode(root, 3);
}
main();
Time Complexity: O(H), explanation is that the time complexity is dependent on the height of the tree.
Space Complexity: O(H), explanation is that the maximum depth of the recursion call stack is equal to the height of the tree.
Q: Why do we replace the node with its inorder successor or predecessor?
A: The inorder successor (smallest node in the right subtree) or inorder predecessor (largest node in the left subtree) ensures that the BST remains valid while maintaining its properties.
Q: How does this differ from deletion in a normal binary tree?
A: A normal binary tree does not have ordering constraints, so we can delete any node without maintaining order. In a BST, we must carefully restructure after deletion to keep it sorted.
Q: How does this operation affect tree balance?
A: If a balanced tree (like an AVL Tree) is used, it will self-balance after deletion. If a standard BST is used, it may become skewed, requiring rebalancing.
Q: How would you modify the solution to delete multiple values at once?
A: Pass a list of keys and call the deletion function iteratively for each value, ensuring the tree remains valid.