Table of contents
1.
Introduction
2.
Deletion in Threaded Binary Search Tree
3.
Complete Code for Deletion
4.
Frequently Asked Questions 
4.1.
What is a Threaded Binary SearchTree?
4.2.
How do you perform deletion in a Threaded Binary search tree?
4.3.
What are the advantages of a Threaded Binary Tree?
5.
Conclusion
Last Updated: Apr 8, 2024
Medium

Deletion in Threaded Binary Search Tree

Author Mehak Goel
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

A. J. Perlis and C. Thornton proposed a new version of a binary tree called “Threaded Binary Tree” that uses NULL pointers to improve its traversal process. In a threaded binary tree, NULL pointers are replaced by references of other nodes in the tree. These extra references are called threads

Although threads can be deleted in any binary tree, it is most efficient to delete threads in a binary search tree or its variants, i.e. a tree that meets the Binary Search Tree properties. As a result, the Threaded Binary Tree we will use in this blog is a Binary Search Tree having Threads (threaded binary search tree).

Node Structure of Threaded Binary tree


struct Node{
   int data;
   Node *left, *right;
   // false if the right pointer points to inorder successor 
   bool rightThread;
   // false if the left pointer points to inorder predecessor 
   bool leftThread;
};
You can also try this code with Online C++ Compiler
Run Code

Before moving directly to Deletion in the Threaded Binary Search Tree, let us first look at the basics of the Threaded Binary Tree

Deletion in Threaded Binary Search Tree

For deletion in the Threaded Binary Search Tree, we will first search the value to be deleted, and then we will see the possible cases for deleting the node in which the value is present. 

// For deleting the value from threaded BST with the given root 
// and returning a new root of BST

struct Node* dThreadedBST(struct Node* root, int value)
{
   // Function for initializing the parent node to NULL and current node to root.
   struct Node *parent  = NULL, *current = root;

   // Set true if value is found
   int found = 0;

   // Now, we will search value in BST 
   while (current != NULL) {
       if (value == current->data) {
           found = 1;
           break;
       }
       parent  = current;
       if (value < current->data) {
           if (current->leftThread == false)
               current = current->left;
           else
               break;
       }
       else {
           if (current->rightThread == false)
               current = current->right;
           else
                 break;
       }
   }

   if (found == 0)
       printf("The value is not present in tree\n");

   // When the node to be deleted has two children
   else if (current->leftThread == false && current->rightThread == false)
       root = case3(root, parent , current);

   // When the node to be deleted has only left child
   else if (current->leftThread == false)
       root = case2(root, parent , current);

   // When the node to be deleted has only right child
   else if (current->rightThread == false)
       root = case2(root, parent , current);

   // When a leaf node needs to be deleted
   else
       root = case1(root, parent , current);

   return root;
}
You can also try this code with Online C++ Compiler
Run Code

We can use the delete operation to remove a node from a threaded binary search tree. However, there are three cases for removing it.

Case 1: When a leaf node needs to be deleted

When deleting a leaf Node in BST, the left or right pointer of the parent node is set to NULL. Whereas in Threaded binary search trees, it is turned into a thread instead of setting the pointer to NULL.

If the leaf Node is the left child of its parent, then after deletion, the parent’s left pointer should become a thread referring to its predecessor. 

Let’s understand this using an example.

 

In the tree given above, if we delete node 18, then the left pointer of its parent node, node 20, will point to its predecessor node 15 (as shown below).

 

Similarly, If the leaf Node is the right child of its parent, then after deletion, the parent’s right pointer should become a thread referring to its successor. After deletion, the Node that was the in-order successor of the leaf Node will now become the in-order successor of the parent Node.

Now, let’s have a look at the code:

// Here, 'parent ' is a pointer referring to the parent node 
// and 'current' is a pointer to the current node.

struct Node* case1 (struct Node* root, struct Node* parent, struct Node* current)                  
{
   // If root node has to be deleted
   if (parent  == NULL)
       root = NULL;

   // If the node to be deleted is left child of its parent
   else if (current == parent ->left) {
       parent ->leftThread = true;
       parent ->left = current->left;
   }

   // If the node to be deleted is the right child of its parent
   else {
       parent ->rightThread = true;
       parent ->right = current->right;
   }

   // Now memory is freed, and new root is returned
   free(current);
   return root;
}
You can also try this code with Online C++ Compiler
Run Code

 

Case 2: When the node to be deleted has only one child

After deleting the Node like in a BST the inorder successor and predecessor of the Node are found. 

s = inSucc(current);

p = inPred(current);
You can also try this code with Online C++ Compiler
Run Code

If Node to be deleted has a left subtree, then after deletion, the right thread of its predecessor should point to its successor. 

Let’s understand this using an example.

 

In the figure given above, node 24 has node 23 as its predecessor and node 30 as its successor. So, after deleting node 24, node 30 becomes the successor of node 23, so the right thread of 23 will point to 30 (as shown in the figure given below).

 

Similarly, if Node to be deleted has a right subtree, then after deletion, the left thread of its successor should point to its predecessor. 

Now, let’s have a look at the code:

// Here, 'parent ' is a pointer referring to the parent node 
// and 'current' is a pointer to the current node.

struct Node* case2 (struct Node* root, struct Node* parent, struct Node* current)
{
   struct Node* child;

   // First initialize whether the child Node to be deleted has left child.
   if (current->leftThread == false)
       child = current->left;

   // or a right child
   else
       child = current->right;

   // If root node has to be deleted
   if (parent  == NULL)
       root = child;

   // If the node to be deleted is left child of its parent
   else if (current == parent ->left)
       parent ->left = child;

   // If the node to be deleted is right child of its parent
   else
       parent ->right = child;

   // Find successor and predecessor
   Node* s = inSucc(current);
   Node* p = inPred(current);

   // If current node has left subtree
   if (current->leftThread == false)
       p->right = s;

   // If current node has right subtree.
   else {
       if (current->rightThread == false)
           s->left = p;
   }

   // Now memory is freed and new root is returned
   free(current);
   return root;
}
You can also try this code with Online C++ Compiler
Run Code

 

Case 3: When the node to be deleted has two children

We find the in-order successor of the current Node (Node to be deleted) and then copy the information of this successor into the current Node. After this, the in-order successor Node is deleted using either Case 1 or Case 2.

Let’s have a look at the code:

// Here, 'parent ' is a pointer referring to the parent node 
// and 'current' is a pointer to the current node
struct Node* case3(struct Node* root, struct Node* parent,  struct Node* current)
{
   // Find the inorder successor 
   struct Node* parsucc = current;
   struct Node* succ = current->right;

   // Find the leftmost child 
   while (succ->leftThread==false) {
       parsucc = succ;
       succ = succ->left;
   }

   current->data = succ->data;

   if (succ->leftThread == true && succ->rightThread == true)
       root = case1(root, parsucc, succ);
   else
       root = case2(root, parsucc, succ);

   return root;
}
You can also try this code with Online C++ Compiler
Run Code

 

Complete Code for Deletion

#include <bits/stdc++.h>
using namespace std;

struct Node {
   struct Node *left, *right;
   int data;

   // false if the left pointer points to a predecessor of in Inorder Traversal
   bool leftThread;

   // false if the right pointer points to a predecessor of in Inorder Traversal
   bool rightThread;
};

// For inserting a Node in Threaded Binary Tree
struct Node* insert(struct Node* root, int key)
{
   // Searching for a Node with given value
   Node* current = root;
   Node* parent = NULL;
   while (current != NULL) {
       // If value already exists, then we will return the root
       if (key == (current->data)) {
           printf("Duplicate value !\n");
           return root;
       }

       // Updating the parent pointer
       parent = current;  
       // Taking the left subtree
       if (key < current->data) {
           if (current->leftThread == false)
               current = current->left;
           else
               break;
       }

       // Taking the right subtree
       else {
           if (current->rightThread == false)
               current = current->right;
           else
               break;
       }
   }

   // Creating a new Node
   Node* temp = new Node;
   temp->data = key;
   temp->leftThread = true;
   temp->rightThread = true;

   if (parent == NULL) {
       root = temp;
       temp->left = NULL;
       temp->right = NULL;
   }
   else if (key < (parent->data)) {
       temp->left = parent->left;
       temp->right = parent;
       parent->leftThread = false;
       parent->left = temp;
   }
   else {
       temp->left = parent;
       temp->right = parent->right;
       parent->rightThread = false;
       parent->right = temp;
   }

   return root;
}

// For returning the inorder successor using left and right children 
struct Node* inSucc(struct Node* current)
{
   if (current->rightThread == true)
       return current->right;

   current = current->right;
   while (current->leftThread == false)
       current = current->left;

   return current;
}

// For returning inorder successor using rightThread 
struct Node* inorderSuccessor(struct Node* current)
{
   // If rightThread is set, we can quickly find
   if (current->rightThread == true)
       return current->right;

   // Or return the leftmost child of right subtree
   current = current->right;
   while (current->leftThread == false)
       current = current->left;
   return current;
}

// Print the threaded tree
void inorder(struct Node* root)
{
   if (root == NULL)
       printf("Tree is empty");

   // Taking leftmost Node
   struct Node* current = root;
   while (current->leftThread == false)
       current = current->left;

   while (current != NULL) {
       printf("%d ", current->data);
       current = inorderSuccessor(current);
   }
}

struct Node* inPred(struct Node* current)
{
   if (current->leftThread == true)
       return current->left;

   current = current->left;
   while (current->rightThread == false)
       current = current->right;
   return current;
}

// Here 'parent ' is a pointer referring to the parent node 
// and 'current' is a pointer to the current node.
struct Node* case1(struct Node* root, struct Node* parent,struct Node* current)
{
   // If root node has to be deleted
   if (parent == NULL)
       root = NULL;

   // If the node to be deleted is left child of its parent
   else if (current == parent->left) {
       parent->leftThread = true;
       parent->left = current->left;
   }

   // If the node to be deleted is the right child of its parent
   else {
       parent->rightThread = true;
       parent->right = current->right;
   }

   // Now memory is freed and new root is returned
   free(current);
   return root;
}

// Here 'parent ' is a pointer referring to the parent node 
// and 'current' is a pointer to the current node.
struct Node* case2 (struct Node* root, struct Node* parent, struct Node* current)               
{
   struct Node* child;
   // First initialize whether the child Node to be deleted has left child.
   if (current->leftThread == false)
       child = current->left;

   // or a right child
   else
       child = current->right;

   // If root node has to be deleted
   if (parent  == NULL)
       root = child;

   // If the node to be deleted is left child of its parent
   else if (current == parent ->left)
       parent ->left = child;

   // If the node to be deleted is right child of its parent
   else
       parent ->right = child;

   // Find successor and predecessor
   Node* s = inSucc(current);
   Node* p = inPred(current);

   // If current node has left subtree
   if (current->leftThread == false)
       p->right = s;

   // If current node has right subtree.
   else {
       if (current->rightThread == false)
           s->left = p;
   }

   // Now memory is freed and new root is returned
   free(current);
   return root;
}

// Here 'parent' is a pointer referring to the parent node 
// and 'current' is a pointer to the current node
struct Node* case3(struct Node* root, struct Node* parent,  struct Node* current)
{
   // Find the inorder successor 
   struct Node* parsucc = current;
   struct Node* succ = current->right;

   // Find the leftmost child 
   while (succ->leftThread==false) {
       parsucc = succ;
       succ = succ->left;
   }

   current->data = succ->data;

   if (succ->leftThread == true && succ->rightThread == true)
       root = case1(root, parsucc, succ);
   else
       root = case2(root, parsucc, succ);

   return root;
}

// For deleting the value from threaded BST with the given root 
// and returning a new root of BST

struct Node* dThreadedBST(struct Node* root, int value)
{
   // For initializing the parent node as NULL and current node as root.
   struct Node *parent  = NULL, *current = root;

   // Set true if value is found
   int found = 0;

   // Searching value in BST 
   while (current != NULL) {
       if (value == current->data) {
           found = 1;
           break;
       }
       parent  = current;
       if (value < current->data) {
           if (current->leftThread == false)
               current = current->left;
           else
               break;
       }
       else {
           if (current->rightThread == false)
               current = current->right;
           else
               break;
       }
   }

   if (found == 0)
       printf("The value not present in tree\n");

   // When the node to be deleted has two children
   else if (current->leftThread == false && current->rightThread == false)
       root = case3(root, parent , current);

   // When the node to be deleted has only left child
   else if (current->leftThread == false)
       root = case2(root, parent , current);

   // When the node to be deleted has only right child
   else if (current->rightThread == false)
       root = case2(root, parent , current);

   // When a leaf node needs to be deleted
   else
       root = case1(root, parent , current);

   return root;
}
// Driver Program
int main()
{
   struct Node* root = NULL;

   root = insert(root, 45);
   root = insert(root, 8);
   root = insert(root, 24);
   root = insert(root, 20);
   root = insert(root, 15);
   root = insert(root, 30);
   root = insert(root, 23);
   root = insert(root, 18);

   root = dThreadedBST(root, 24);
   inorder(root);

   return 0;
}

You can also try this code with Online C++ Compiler
Run Code

Output

8 15 18 20 23 30 45
You can also try this code with Online C++ Compiler
Run Code

Time complexity – O(h), where h is the height of the threaded binary search tree. The height of the tree can be ‘n’ in the worst case, and all the keys are deleted in ascending or descending order (Skewed Tree) where all the nodes except the leaf have only one child, and we may have to travel from root to the deepest leaf node.

Space complexity – O(1), as we are not using any additional auxiliary space.

Check out this problem - Mirror A Binary Tree.

Must Read Recursive Binary Search.

Frequently Asked Questions 

What is a Threaded Binary SearchTree?

A threaded binary search tree is a variation of a normal binary search tree. In a threaded binary search tree, if the left pointer of a node is NULL, it is pointed to its in-order predecessor (if it exists), and if the right pointer of a node is NULL, it is pointed to its in-order successor (if it exists) using links called as threads.

How do you perform deletion in a Threaded Binary search tree?

Yes, we can perform deletion in a threaded binary search tree like it is performed in a binary search tree. First, we will search the value to be deleted and then see the possible cases for deleting it. There are mainly three cases when performing deletion:
1. When a leaf node is to be deleted.
2. When the node to be deleted has only one child.
3. When the node to be deleted has two children.

What are the advantages of a Threaded Binary Tree?

Some advantages of a Threaded Binary tree are:
1. They do not require a stack or recursion for their traversal.
2. In threaded binary trees, memory is not wasted as whenever a node’s left/right pointer is NULL, its inorder predecessor/successor is stored.
3. We can do a backward traversal in a double-threaded binary tree.
4. In-order traversal is fast in a threaded binary tree than a normal binary tree.

Conclusion

In this article, we learned about ways to delete a node in threaded binary search trees. Majorly three prominent cases arrive when we try to delete a node. The first one is when the node is a leaf node, the second one when the node to be deleted has only one child (that means left or right pointer is not NULL), and the third one is when the node has two children (that means both left and right child pointers are not NULL). Hence, we traversed the tree, keeping in mind all three cases.

Recommended problems -

Live masterclass