Binary Search Tree

It's just binary tree with some logic! Here our tree has some properties like, our root will be greater than everything of it's left and will be less than it's right subtree and so on... So why? What's the point?

Well, here's the thing, it'll let us find either an elelment exist in our tree or not in O(log_2 n) time complexity. Pretty kool, right?

7.4 Find

Before inserting values, let's actually try to find a value exists in our tree or not. We will iterate through our tree to find the location and previous location of whatever we search for. So if our location is NULL we can just go ahead and use that information while inserting.

void find(Node *&root, Node *&location, Node *&previous, int item) {
    location = root;
    previous = NULL;
    if (location == NULL) {
        return;
    }
    while (location != NULL) {
        if (item == location -> data) {
            return;
        } 
        if (item < location -> data) {
            previous = location;
            location = location -> left;
        }
        else {
            previous = location;
            location = location -> right;
        }
    }
}

Here we are passing pointer as a reference! Pretty kool, right? We will be using these values later!

7.5 Insert

In insertion we will simply use the data of above method. And as always if it's NULL we will try to add that node.

void insert(Node *&root, int item) {
    Node *location, *previous;
    find(root, location, previous, item);
    if (location != NULL) return;
    if (previous == NULL)
        root = new Node(item);
    else if (item < previous -> data) 
        previous -> left = new Node(item);
    else 
        previous -> right = new Node(item);
}

Suppose we have the location of a node and the location of it's parent node and we are gonna delete that node.

7.6 Delete with one child

We will be deleting nodes if any node of our tree has only one child. So we can just go ahead and check either it has a left child or right child and act accordingly.

void delete_with_one_child(Node *&root, Node *&location, Node *&parent) {
    Node *child;
    if (location -> left != NULL) 
        child = location -> left;
    else if (location -> right != NULL)
        child = location -> right;
    else 
        child = NULL;
    
    if (parent != NULL) {
        if (location == parent -> left) 
            parent -> left = child;
        else 
            parent -> right = child;
    } else {
        root = child;
    }
}

But what if we have two child?

7.7 Delete with two child

In this case, things are bit tricky, cause we have to consider which of the successor will take the place of the node that we delete. And actually if we iterate through the right node's left element and so on, we will eventually meet with that guy. Next we can use the above trick to get rid of that alongside joining our new node with parent.

void delete_with_two_children(Node *&root, Node *&location, Node *&parent) {
    Node *successor, *parent_successor;
    parent_successor = location;
    successor = location -> right;
    while (successor -> left != NULL) {
        parent_successor = successor;
        successor = successor -> left;
    }
    delete_with_one_child(root, successor, parent_successor);
    if (parent != NULL) {
        if (location == parent -> left) 
            parent -> left = successor;
        else 
            parent -> right = successor;
    } else {
        root = successor;
    }
    successor -> left = location -> left;
    successor -> right = location -> right;
}

7.8 Deleting an element

As we have seen in the 7.6 and 7.7, we will be needing address of the node that we want to delete. And here in this step we will work around this. We will use 7.4 to find the address of our node and parent and will try to determine either it has one child or two child!

void delete_node(Node *&root, int item) {
    Node *location, *parent;
    find(root, location, parent, item);
    if (location == NULL) return;
    if (location -> left != NULL && location -> right != NULL) 
        delete_with_two_children(root, location, parent);
    else 
        delete_with_one_child(root, location, parent);
    delete location;
}

Wrapping up!

After combining all of the above algorithms here's a full executable code,

#include <iostream>
#include <stack>
#include <queue>

using namespace std;

struct Node {
    int data;
    Node* left;
    Node* right;
    Node(int data) {
        this->data = data;
        left = NULL;
        right = NULL;
    }
};

void find(Node *&root, Node *&location, Node *&previous, int item) {
    location = root;
    previous = NULL;
    if (location == NULL) {
        return;
    }
    while (location != NULL) {
        if (item == location -> data) {
            return;
        } 
        if (item < location -> data) {
            previous = location;
            location = location -> left;
        }
        else {
            previous = location;
            location = location -> right;
        }
    }
}

void insert(Node *&root, int item) {
    Node *location, *previous;
    find(root, location, previous, item);
    if (location != NULL) return;
    if (previous == NULL)
        root = new Node(item);
    else if (item < previous -> data) 
        previous -> left = new Node(item);
    else 
        previous -> right = new Node(item);
}

void delete_with_one_child(Node *&root, Node *&location, Node *&parent) {
    Node *child;
    if (location -> left != NULL) 
        child = location -> left;
    else if (location -> right != NULL)
        child = location -> right;
    else 
        child = NULL;
    
    if (parent != NULL) {
        if (location == parent -> left) 
            parent -> left = child;
        else 
            parent -> right = child;
    } else {
        root = child;
    }
}

void delete_with_two_children(Node *&root, Node *&location, Node *&parent) {
    Node *successor, *parent_successor;
    parent_successor = location;
    successor = location -> right;
    while (successor -> left != NULL) {
        parent_successor = successor;
        successor = successor -> left;
    }
    delete_with_one_child(root, successor, parent_successor);
    if (parent != NULL) {
        if (location == parent -> left) 
            parent -> left = successor;
        else 
            parent -> right = successor;
    } else {
        root = successor;
    }
    successor -> left = location -> left;
    successor -> right = location -> right;
}

void delete_node(Node *&root, int item) {
    Node *location, *parent;
    find(root, location, parent, item);
    if (location == NULL) return;
    if (location -> left != NULL && location -> right != NULL) 
        delete_with_two_children(root, location, parent);
    else 
        delete_with_one_child(root, location, parent);
    delete location;
}

void preorder(Node *root) {
    if (root == NULL) return;
    cout << root -> data << " ";
    preorder(root -> left);
    preorder(root -> right);
}

void inorder(Node *root) {
    if (root == NULL) return;
    inorder(root -> left);
    cout << root -> data << " ";
    inorder(root -> right);
}

void level_order(Node *root) {
    if (root == NULL) return;
    queue<Node*> q;
    q.push(root);
    while (!q.empty()) {
        Node *current = q.front();
        q.pop();
        cout << current -> data << " ";
        if (current -> left != NULL) q.push(current -> left);
        if (current -> right != NULL) q.push(current -> right);
    }
}

int main() {
    Node* tree = NULL;
    insert(tree, 60);
    insert(tree, 25);
    insert(tree, 75);
    insert(tree, 15);
    insert(tree, 50);
    insert(tree, 66);
    insert(tree, 33);
    insert(tree, 44);

    cout << "Before Deletion -> \n";
    cout << "Preorder -> \n";
    preorder(tree);
    cout << "\nInorder -> \n";
    inorder(tree);
    cout << "\nLevel Order -> \n";

    delete_node(tree, 25);

    cout << "After Deletion -> \n";
    cout << "Preorder -> \n";
    preorder(tree);
    cout << "\nInorder -> \n";
    inorder(tree);
    cout << "\nLevel Order -> \n";
    level_order(tree);
}

Last updated

Was this helpful?