C++ Program to Implement B+ Tree



A B+ tree is an m-tree that consists of a root, internal nodes, and leaves. The root may be a leaf or a node with two or more children. A B+ tree is an advanced data structure that extends the B-tree by adding a linked list of leaf nodes.

A B+ tree can be a B-tree where each node contains only keys (not key-value pairs).

What is B+ Tree?

A B+ tree is a self-balancing tree data structure that maintains sorted data and allows for efficient insertion, deletion, and search operations. It differs from a B-tree in the following ways:

  • All values are at the leaf level.
  • Leaf nodes are linked, making range queries and sequential access more efficient.

Following is the diagram of B+ tree:

b_puls_tree

Properties of B+ Tree

A B+ tree of order m has the following properties:

  • All leaf node at the same level.
  • All internal nodes except the root have at least [m/2] Children. But the root has at least two children it is not a leaf node.
  • All internal nodes have at most m children.
  • Each internal node with k children contains k-1 keys.
  • All leaf nodes contain between [m/2] and m keys.
  • The keys in leaf nodes are sorted, and each leaf node has a pointer to the next leaf node.

Implement B+ Tree in C++

A B+ tree contains internal nodes and leaf nodes. The internal node contains a key and a pointer to the child node, while the leaf node stores keys and pointers to the actual data values.

To construct a B+ tree in C++, we will use a class that contains the required definition: A struct to represent each node and a member function to provide basic functionality.

template <typename T>
class B_plus_tree {
public:
   struct Node {
      bool isLeaf;
      vector<T> keys;
      vector<Node*> children;
      Node* next;
   };

Operations of B+ Tree

The following are some of the basic operations of the B+ tree that are needed to manipulate its elements.

Operation Description Time Complexity
Insert Insert a new element into the B+ tree. O(logn)
Delete Remove an element from the B+ tree. O(logn)
Search Searches for the element in the B+ tree. O(logn)
Range Query Retrieve all elements within the given range from the B+ Tree. O(logn+k)
Split Child It split a full child node during insertion. O(1)

Example

In the following example, we demonstrate the implementation of a B+ tree in C++ -

#include <algorithm>
#include <iostream>
#include <vector>

using namespace std;

template < typename T > class B_plus_tree {
   public:
      // structure to create a node
      struct Node {
         bool isLeaf;
         vector < T > keys;
         vector < Node * > children;
         Node * next;

         Node(bool leaf = false): isLeaf(leaf), next(nullptr) {}
      };

   Node * root;
   int t;

   // Function for split
   void splitChild(Node * parent, int index, Node * child);

   // Function for insert
   void insertNonFull(Node * node, T key);

   // Function for remove
   void remove(Node * node, T key);

   // Function for borrow
   void borrowFromPrev(Node * node, int index);

   // Function for borrow a key from the next sibling
   void borrowFromNext(Node * node, int index);

   // Function for merge two nodes
   void merge(Node * node, int index);

   void printTree(Node * node, int level);

   public: B_plus_tree(int degree): root(nullptr),
   t(degree) {}

   void insert(T key);
   bool search(T key);
   void remove(T key);
   vector < T > rangeQuery(T lower, T upper);
   void printTree();
};

// Implementation of splitChild function
template < typename T >
   void B_plus_tree < T > ::splitChild(Node * parent, int index,
      Node * child) {
      Node * newChild = new Node(child -> isLeaf);
      parent -> children.insert(
         parent -> children.begin() + index + 1, newChild);
      parent -> keys.insert(parent -> keys.begin() + index,
         child -> keys[t - 1]);

      newChild -> keys.assign(child -> keys.begin() + t,
         child -> keys.end());
      child -> keys.resize(t - 1);

      if (!child -> isLeaf) {
         newChild -> children.assign(child -> children.begin() +
            t,
            child -> children.end());
         child -> children.resize(t);
      }

      if (child -> isLeaf) {
         newChild -> next = child -> next;
         child -> next = newChild;
      }
   }

// Implementation of insertNonFull function
template < typename T >
   void B_plus_tree < T > ::insertNonFull(Node * node, T key) {
      if (node -> isLeaf) {
         node -> keys.insert(upper_bound(node -> keys.begin(),
               node -> keys.end(),
               key),
            key);
      } else {
         int i = node -> keys.size() - 1;
         while (i >= 0 && key < node -> keys[i]) {
            i--;
         }
         i++;
         if (node -> children[i] -> keys.size() == 2 * t - 1) {
            splitChild(node, i, node -> children[i]);
            if (key > node -> keys[i]) {
               i++;
            }
         }
         insertNonFull(node -> children[i], key);
      }
   }

// Implementation of remove function
template < typename T >
   void B_plus_tree < T > ::remove(Node * node, T key) {
      // If node is a leaf
      if (node -> isLeaf) {
         auto it = find(node -> keys.begin(), node -> keys.end(),
            key);
         if (it != node -> keys.end()) {
            node -> keys.erase(it);
         }
      } else {
         int idx = lower_bound(node -> keys.begin(),
               node -> keys.end(), key) -
            node -> keys.begin();
         if (idx < node -> keys.size() &&
            node -> keys[idx] == key) {
            if (node -> children[idx] -> keys.size() >= t) {
               Node * predNode = node -> children[idx];
               while (!predNode -> isLeaf) {
                  predNode = predNode -> children.back();
               }
               T pred = predNode -> keys.back();
               node -> keys[idx] = pred;
               remove(node -> children[idx], pred);
            } else if (node -> children[idx + 1] -> keys.size() >=
               t) {
               Node * succNode = node -> children[idx + 1];
               while (!succNode -> isLeaf) {
                  succNode = succNode -> children.front();
               }
               T succ = succNode -> keys.front();
               node -> keys[idx] = succ;
               remove(node -> children[idx + 1], succ);
            } else {
               merge(node, idx);
               remove(node -> children[idx], key);
            }
         } else {
            if (node -> children[idx] -> keys.size() < t) {
               if (idx > 0 &&
                  node -> children[idx - 1] -> keys.size() >= t) {
                  borrowFromPrev(node, idx);
               } else if (idx < node -> children.size() - 1 &&
                  node -> children[idx + 1] -> keys.size() >= t) {
                  borrowFromNext(node, idx);
               } else {
                  if (idx < node -> children.size() - 1) {
                     merge(node, idx);
                  } else {
                     merge(node, idx - 1);
                  }
               }
            }
            remove(node -> children[idx], key);
         }
      }
   }

// Implementation of borrowFromPrev function
template < typename T >
   void B_plus_tree < T > ::borrowFromPrev(Node * node, int index) {
      Node * child = node -> children[index];
      Node * sibling = node -> children[index - 1];

      child -> keys.insert(child -> keys.begin(),
         node -> keys[index - 1]);
      node -> keys[index - 1] = sibling -> keys.back();
      sibling -> keys.pop_back();

      if (!child -> isLeaf) {
         child -> children.insert(child -> children.begin(),
            sibling -> children.back());
         sibling -> children.pop_back();
      }
   }

// Implementation of borrowFromNext function
template < typename T >
   void B_plus_tree < T > ::borrowFromNext(Node * node, int index) {
      Node * child = node -> children[index];
      Node * sibling = node -> children[index + 1];

      child -> keys.push_back(node -> keys[index]);
      node -> keys[index] = sibling -> keys.front();
      sibling -> keys.erase(sibling -> keys.begin());

      if (!child -> isLeaf) {
         child -> children.push_back(
            sibling -> children.front());
         sibling -> children.erase(sibling -> children.begin());
      }
   }

// Implementation of merge function
template < typename T >
   void B_plus_tree < T > ::merge(Node * node, int index) {
      Node * child = node -> children[index];
      Node * sibling = node -> children[index + 1];

      child -> keys.push_back(node -> keys[index]);
      child -> keys.insert(child -> keys.end(),
         sibling -> keys.begin(),
         sibling -> keys.end());
      if (!child -> isLeaf) {
         child -> children.insert(child -> children.end(),
            sibling -> children.begin(),
            sibling -> children.end());
      }

      node -> keys.erase(node -> keys.begin() + index);
      node -> children.erase(node -> children.begin() + index +
         1);

      delete sibling;
   }

// Implementation of printTree function
template < typename T >
   void B_plus_tree < T > ::printTree(Node * node, int level) {
      if (node != nullptr) {
         for (int i = 0; i < level; ++i) {
            cout << "  ";
         }
         for (const T & key: node -> keys) {
            cout << key << " ";
         }
         cout << endl;
         for (Node * child: node -> children) {
            printTree(child, level + 1);
         }
      }
   }

// Implementation of printTree wrapper function
template < typename T > void B_plus_tree < T > ::printTree() {
   printTree(root, 0);
}

// Implementation of search function
template < typename T > bool B_plus_tree < T > ::search(T key) {
   Node * current = root;
   while (current != nullptr) {
      int i = 0;
      while (i < current -> keys.size() &&
         key > current -> keys[i]) {
         i++;
      }
      if (i < current -> keys.size() &&
         key == current -> keys[i]) {
         return true;
      }
      if (current -> isLeaf) {
         return false;
      }
      current = current -> children[i];
   }
   return false;
}

// Implementation of range query function
template < typename T >
   vector < T > B_plus_tree < T > ::rangeQuery(T lower, T upper) {
      vector < T > result;
      Node * current = root;
      while (!current -> isLeaf) {
         int i = 0;
         while (i < current -> keys.size() &&
            lower > current -> keys[i]) {
            i++;
         }
         current = current -> children[i];
      }
      while (current != nullptr) {
         for (const T & key: current -> keys) {
            if (key >= lower && key <= upper) {
               result.push_back(key);
            }
            if (key > upper) {
               return result;
            }
         }
         current = current -> next;
      }
      return result;
   }

// Implementation of insert function
template < typename T > void B_plus_tree < T > ::insert(T key) {
   if (root == nullptr) {
      root = new Node(true);
      root -> keys.push_back(key);
   } else {
      if (root -> keys.size() == 2 * t - 1) {
         Node * newRoot = new Node();
         newRoot -> children.push_back(root);
         splitChild(newRoot, 0, root);
         root = newRoot;
      }
      insertNonFull(root, key);
   }
}

// Implementation of remove function
template < typename T > void B_plus_tree < T > ::remove(T key) {
   if (root == nullptr) {
      return;
   }
   remove(root, key);
   if (root -> keys.empty() && !root -> isLeaf) {
      Node * tmp = root;
      root = root -> children[0];
      delete tmp;
   }
}

// Main function to test the B+ Tree implementation
int main() {
   B_plus_tree < int > tree(3);

   // Insert elements
   tree.insert(1);
   tree.insert(2);
   tree.insert(5);
   tree.insert(7);
   tree.insert(9);
   tree.insert(11);

   cout << "B+ Tree after insertions:" << endl;
   tree.printTree();

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

   // Perform a range query
   int lower = 1, upper = 11;
   vector < int > rangeResult = tree.rangeQuery(lower, upper);
   cout << "\nRange query [" << lower << ", " << upper <<
      "]: ";
   for (int key: rangeResult) {
      cout << key << " ";
   }
   cout << endl;

   // Remove a key
   int removeKey = 7;
   tree.remove(removeKey);
   cout << "\nB+ Tree after removing " << removeKey << ":" <<
      endl;
   tree.printTree();

   return 0;
}

Following is the B+ tree -

B+ Tree after insertions:
5 
  1 2 
  7 9 11 

Searching for key 9: Found

Range query [1, 11]: 1 2 7 9 11 

B+ Tree after removing 7:
5 
  1 2 
  9 11
Updated on: 2025-05-16T17:14:30+05:30

3K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements