Open In App

Balanced Binary Tree in C++

Last Updated : 18 Jul, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

A Balanced Binary Tree, also known as a height-balanced binary tree, is a binary tree in which the height of every node's left and right subtrees differs by at most one. This balance condition ensures that the tree maintains a logarithmic height, which in turn guarantees O(log n) time complexity for operations such as insertion, deletion, and search.

In this article, we will learn how to implement a Balanced Binary Tree in C++ and its basic operations.

What is a Balanced Binary Tree?

A Balanced Binary Tree is a binary tree where the heights of the left and right subtrees of any node differ by at most one. This balance condition is maintained through rotations during insertions and deletions.

Properties of Balanced Binary Trees

  • The height difference between the left and right subtrees of any node is at most 1.
  • Both the left and right subtrees are also balanced binary trees.
  • The height of an empty tree is considered -1.
  • The height of a tree with one node is 0.
balancedvsunbalancedBinaryTree
Balanced vs Unbalanced Binary Tree

Implementation of Balanced Binary Tree in C++ Language

A Balanced Binary Tree can be implemented using a self-balancing binary search tree, such as an AVL tree or a Red-Black tree. For this implementation, we'll use an AVL tree, which maintains balance by storing height information in each node and performing rotations when necessary.

Representation of Balanced Binary Tree in C++

To represent a balanced binary tree we will implement a class Node that will represent a single node of the balanced binary tree and BalancedBinaryTree class that will contain all the required member functions. We will use template to keep the balanced binary tree generic so that it can support multiple data types.

template <typename T>
class Node {
public:
T data;
Node* left;
Node* right;
int height;
};

Here,

  • data: stores the value in the node.
  • left and right: store pointers to the left and right child nodes.
  • height: stores the height of the subtree rooted at this node.

The following diagram represents the structure of a balanced binary tree:

Balanced-Binary-Tree
Balanced Binary Tree

Basic Operations of Balanced Binary Tree in C++

Following are some of the basic operations of a Balanced Binary Tree that are required to manipulate its elements:

Operation

Description

Time Complexity

Space Complexity

Insert

Inserts a new element into the tree

O(log n)

O(1)

Delete

Removes an element from the tree

O(log n)

O(1)

Search

Searches for an element in the tree

O(log n)

O(1)

Height

Calculates the height of a node

O(1)

O(1)

Balance Factor

Calculates the balance factor of a node

O(1)

O(1)

Rotate Right

Performs a right rotation on a node

O(1)

O(1)

Rotate Left

Performs a left rotation on a node

O(1)

O(1)

Here, n represents the number of nodes in the tree.

Implementation of Insert Function

  • Perform a standard BST insertion.
  • Update the height of the ancestors nodes.
  • Check the balance factor of the ancestors.
  • If the node becomes unbalanced, perform one of the four rotations:
    • Left-Left Case: Right Rotation
    • Left-Right Case: Left Rotation followed by Right Rotation
    • Right-Right Case: Left Rotation
    • Right-Left Case: Right Rotation followed by Left Rotation

Implementation of Delete Function

  • Perform a standard BST deletion.
  • Update the height of the ancestors nodes.
  • Check the balance factor of the ancestors.
  • If the node becomes unbalanced, perform one of the four rotations as in insertion.

Implementation of Height and Balance Factor Functions

  • Height: Return the height stored in the node, or 0 if the node is null.
  • Balance Factor: Calculate the difference between the heights of left and right subtrees and return the calculated value.

Implementation of Rotation Functions

  • Right Rotation:
    • Make the left child of the current node the new root of the subtree.
    • Make the old root the right child of the new root.
    • Update the heights of the nodes.
  • Left Rotation:
    • Make the right child of the current node the new root of the subtree.
    • Make the old root the left child of the new root.
    • Update the heights of the nodes.

C++ Program to Implement Balanced Binary Tree

The following program demonstrates the implementation of a Balanced Binary Tree in C++.

C++
// C++ Program to Implement Balanced Binary Tree
#include <algorithm>
#include <iostream>
using namespace std;

template <typename T> class BalancedBinaryTree {
private:
    // Node structure definition
    struct Node {
        T data;
        Node* left;
        Node* right;
        int height;

        Node(T value)
            : data(value)
            , left(nullptr)
            , right(nullptr)
            , height(1)
        {
        }
    };

    Node* root;

    // Function to get the height of the node
    int height(Node* node)
    {
        return node ? node->height : 0;
    }

    // Function to get the balance factor of the node
    int balanceFactor(Node* node)
    {
        return node ? height(node->left)
                          - height(node->right)
                    : 0;
    }

    // Function to update the height of the node
    void updateHeight(Node* node)
    {
        node->height = 1
                       + max(height(node->left),
                             height(node->right));
    }

    // Right rotation function
    Node* rotateRight(Node* y)
    {
        Node* x = y->left;
        Node* T2 = x->right;

        x->right = y;
        y->left = T2;

        updateHeight(y);
        updateHeight(x);

        return x;
    }

    // Left rotation function
    Node* rotateLeft(Node* x)
    {
        Node* y = x->right;
        Node* T2 = y->left;

        y->left = x;
        x->right = T2;

        updateHeight(x);
        updateHeight(y);

        return y;
    }

    // Function to insert a node
    Node* insert(Node* node, T key)
    {
        // Perform the normal BST insertion
        if (!node)
            return new Node(key);

        if (key < node->data)
            node->left = insert(node->left, key);
        else if (key > node->data)
            node->right = insert(node->right, key);
        else
            return node; // Duplicate keys are not allowed

        // Update height of this ancestor node
        updateHeight(node);

        // Get the balance factor to check whether this node
        // became unbalanced
        int balance = balanceFactor(node);

        // If the node becomes unbalanced, there are 4 cases

        // Left Left Case
        if (balance > 1 && key < node->left->data)
            return rotateRight(node);

        // Right Right Case
        if (balance < -1 && key > node->right->data)
            return rotateLeft(node);

        // Left Right Case
        if (balance > 1 && key > node->left->data) {
            node->left = rotateLeft(node->left);
            return rotateRight(node);
        }

        // Right Left Case
        if (balance < -1 && key < node->right->data) {
            node->right = rotateRight(node->right);
            return rotateLeft(node);
        }

        return node;
    }

    // Function to find the node with the minimum value
    // (used in deletion)
    Node* findMin(Node* node)
    {
        while (node->left)
            node = node->left;
        return node;
    }

    // Function to delete a node
    Node* remove(Node* node, T key)
    {
        // Perform standard BST delete
        if (!node)
            return nullptr;

        if (key < node->data)
            node->left = remove(node->left, key);
        else if (key > node->data)
            node->right = remove(node->right, key);
        else {
            if (!node->left || !node->right) {
                Node* temp
                    = node->left ? node->left : node->right;
                if (!temp) {
                    temp = node;
                    node = nullptr;
                }
                else
                    *node = *temp;
                delete temp;
            }
            else {
                Node* temp = findMin(node->right);
                node->data = temp->data;
                node->right
                    = remove(node->right, temp->data);
            }
        }

        if (!node)
            return nullptr;

        // Update height of the current node
        updateHeight(node);

        // Get the balance factor
        int balance = balanceFactor(node);

        // Balance the node if it has become unbalanced

        // Left Left Case
        if (balance > 1 && balanceFactor(node->left) >= 0)
            return rotateRight(node);

        // Left Right Case
        if (balance > 1 && balanceFactor(node->left) < 0) {
            node->left = rotateLeft(node->left);
            return rotateRight(node);
        }

        // Right Right Case
        if (balance < -1 && balanceFactor(node->right) <= 0)
            return rotateLeft(node);

        // Right Left Case
        if (balance < -1
            && balanceFactor(node->right) > 0) {
            node->right = rotateRight(node->right);
            return rotateLeft(node);
        }

        return node;
    }

    // Function to search for a key in the tree
    bool search(Node* node, T key)
    {
        if (!node)
            return false;
        if (node->data == key)
            return true;
        if (key < node->data)
            return search(node->left, key);
        return search(node->right, key);
    }

    // Function for inorder traversal of the tree
    void inorderTraversal(Node* node)
    {
        if (node) {
            inorderTraversal(node->left);
            cout << node->data << " ";
            inorderTraversal(node->right);
        }
    }

public:
    // Constructor
    BalancedBinaryTree()
        : root(nullptr)
    {
    }

    // Public insert function
    void insert(T key) { root = insert(root, key); }

    // Public remove function
    void remove(T key) { root = remove(root, key); }

    // Public search function
    bool search(T key) { return search(root, key); }

    // Public function to print the inorder traversal
    void printInorder()
    {
        inorderTraversal(root);
        cout << endl;
    }
};

int main()
{
    BalancedBinaryTree<int> tree;

    // Insert elements
    tree.insert(10);
    tree.insert(20);
    tree.insert(30);
    tree.insert(40);
    tree.insert(50);
    tree.insert(25);

    cout << "Inorder traversal of the constructed Balanced "
            "Binary Tree"
         << endl;
    tree.printInorder();

    // Search for a key
    int searchKey = 30;
    cout << "Searching for key " << searchKey << ": "
         << (tree.search(searchKey) ? "Found" : "Not Found")
         << endl;

    // Remove a key
    int removeKey = 20;
    tree.remove(removeKey);
    cout << "Inorder traversal after removing " << removeKey
         << ": ";
    tree.printInorder();

    return 0;
}

Output
Inorder traversal of the constructed Balanced Binary Tree
10 20 25 30 40 50 
Searching for key 30: Found
Inorder traversal after removing 20: 10 25 30 40 50 

Applications of Balanced Binary Trees

Following are some of the common applications of balanced binary trees:

  • Many database systems use balanced trees (like B-trees, which are a generalization of balanced binary trees) for efficient indexing.
  • Some file systems use balanced trees to organize directory structures.
  • Some memory allocators use balanced trees to keep track of free memory blocks.
  • Balanced binary trees can be used to represent and evaluate arithmetic expressions.
  • Balanced trees can be used to implement efficient lookup tables for network routing.

Next Article
Article Tags :
Practice Tags :

Similar Reads