B-Tree Insert without aggressive splitting
This algorithm for insertion takes an entry, finds the leaf node where it belongs, and inserts it there. We recursively insert the entry by calling the insert algorithm on the appropriate child node. This procedure results in going down to the leaf node where the entry belongs, placing the entry there, and returning all the way back to the root node.
Sometimes a node is full, i.e. it contains 2*t – 1 entries where t is the minimum degree. In such cases the node must be split. In such case one key becomes a parent and a new node is created. We first insert the new key, making total keys as 2*t. We keep the first t entries in original node, transfer last (t-1) entries to new node and set the (t+1)th node as parent of these nodes. If the node being split is a non child node then we also have to split the child pointers. A node having 2*t keys has 2*t + 1 child pointers. The first (t+1) pointers are kept in original node and remaining t pointers goes to new node.
This algorithm splits a node only when it is necessary. We first recursively call insert for appropriate child of node (in case of non-leaf node) or insert it into node (for leaf node). If the node is full, we split it, storing new child entry in newEntry and new parent key in val. These values are then inserted into the parent, which recursively splits itself in case it is also full.
Example:
We insert numbers 1 – 5 in tree. The tree becomes:
Then we insert 6, the node is full. Hence it is split into two nodes making 4 as parent.
We insert numbers 7 – 16, the tree becomes:
We insert 22 – 30, the tree becomes:
Note that now the root is full. If we insert 17 now, the root is not split as the leaf node in which 17 was inserted didn’t split. If we were following aggressive splitting, the root would have been split before we went to leaf node.
But if we insert 31, the leaf node splits, which recursively adds new entry to root. But as root is full, it needs to be split. The tree now becomes.
Below is the implementation of the above approach:
// C++ implementation of the approach #include <iostream> #include <vector> using namespace std;
class BTreeNode {
// Vector of keys
vector< int > keys;
// Minimum degree
int t;
// Vector of child pointers
vector<BTreeNode*> C;
// Is true when node is leaf, else false
bool leaf;
public :
// Constructor
BTreeNode( int t, bool leaf);
// Traversing the node and print its content
// with tab number of tabs before
void traverse( int tab);
// Insert key into given node. If child is split, we
// have to insert *val entry into keys vector and
// newEntry pointer into C vector of this node
void insert( int key, int * val,
BTreeNode*& newEntry);
// Split this node and store the new parent value in
// *val and new node pointer in newEntry
void split( int * val, BTreeNode*& newEntry);
// Returns true if node is full
bool isFull();
// Makes new root, setting current root as its child
BTreeNode* makeNewRoot( int val, BTreeNode* newEntry);
}; bool BTreeNode::isFull()
{ // returns true if node is full
return ( this ->keys.size() == 2 * t - 1);
} BTreeNode::BTreeNode( int t, bool leaf)
{ // Constructor to set value of t and leaf
this ->t = t;
this ->leaf = leaf;
} // Function to print the nodes of B-Tree void BTreeNode::traverse( int tab)
{ int i;
string s;
// Print 'tab' number of tabs
for ( int j = 0; j < tab; j++) {
s += '\t' ;
}
for (i = 0; i < keys.size(); i++) {
// If this is not leaf, then before printing key[i]
// traverse the subtree rooted with child C[i]
if (leaf == false )
C[i]->traverse(tab + 1);
cout << s << keys[i] << endl;
}
// Print the subtree rooted with last child
if (leaf == false ) {
C[i]->traverse(tab + 1);
}
} // Function to split the current node and store the new // parent value is *val and new child pointer in &newEntry // called only for splitting non-leaf node void BTreeNode::split( int * val, BTreeNode*& newEntry)
{ // Create new non leaf node
newEntry = new BTreeNode(t, false );
//(t+1)th becomes parent
*val = this ->keys[t];
// Last (t-1) entries will go to new node
for ( int i = t + 1; i < 2 * t; i++) {
newEntry->keys.push_back( this ->keys[i]);
}
// This node stores first t entries
this ->keys.resize(t);
// Last t entries will go to new node
for ( int i = t + 1; i <= 2 * t; i++) {
newEntry->C.push_back( this ->C[i]);
}
// This node stores first (t+1) entries
this ->C.resize(t + 1);
} // Function to insert a new key in given node. // If child of this node is split, we have to insert *val // into keys vector and newEntry pointer into C vector void BTreeNode::insert( int new_key, int * val,
BTreeNode*& newEntry)
{ // Non leaf node
if (leaf == false ) {
int i = 0;
// Find first key greater than new_key
while (i < keys.size() && new_key > keys[i])
i++;
// We have to insert new_key into left child of
// Node with index i
C[i]->insert(new_key, val, newEntry);
// No split was done
if (newEntry == NULL)
return ;
if (keys.size() < 2 * t - 1) {
// This node can accommodate a new key
// and child pointer entry
// Insert *val into key vector
keys.insert(keys.begin() + i, *val);
// Insert newEntry into C vector
C.insert(C.begin() + i + 1, newEntry);
// As this node was not split, set newEntry
// to NULL
newEntry = NULL;
}
else {
// Insert *val and newentry
keys.insert(keys.begin() + i, *val);
C.insert(C.begin() + i + 1, newEntry);
// Current node has 2*t keys, so split it
split(val, newEntry);
}
}
else {
// Insert new_key in this node
vector< int >::iterator it;
// Find correct position
it = lower_bound(keys.begin(), keys.end(),
new_key);
// Insert in correct position
keys.insert(it, new_key);
// If node is full
if (keys.size() == 2 * t) {
// Create new node
newEntry = new BTreeNode(t, true );
// Set (t+1)th key as parent
*val = this ->keys[t];
// Insert last (t-1) keys into new node
for ( int i = t + 1; i < 2 * t; i++) {
newEntry->keys.push_back( this ->keys[i]);
}
// This node stores first t keys
this ->keys.resize(t);
}
}
} // Function to create a new root // setting current node as its child BTreeNode* BTreeNode::makeNewRoot( int val, BTreeNode* newEntry)
{ // Create new root
BTreeNode* root = new BTreeNode(t, false );
// Stores keys value
root->keys.push_back(val);
// Push child pointers
root->C.push_back( this );
root->C.push_back(newEntry);
return root;
} class BTree {
// Root of B-Tree
BTreeNode* root;
// Minimum degree
int t;
public :
// Constructor
BTree( int t);
// Insert key
void insert( int key);
// Display the tree
void display();
}; // Function to create a new BTree with // minimum degree t BTree::BTree( int t)
{ root = new BTreeNode(t, true );
} // Function to insert a node in the B-Tree void BTree::insert( int key)
{ BTreeNode* newEntry = NULL;
int val = 0;
// Insert in B-Tree
root->insert(key, &val, newEntry);
// If newEntry is not Null then root needs to be
// split. Create new root
if (newEntry != NULL) {
root = root->makeNewRoot(val, newEntry);
}
} // Prints BTree void BTree::display()
{ root->traverse(0);
} // Driver code int main()
{ // Create B-Tree
BTree* tree = new BTree(3);
cout << "After inserting 1 and 2" << endl;
tree->insert(1);
tree->insert(2);
tree->display();
cout << "After inserting 5 and 6" << endl;
tree->insert(5);
tree->insert(6);
tree->display();
cout << "After inserting 3 and 4" << endl;
tree->insert(3);
tree->insert(4);
tree->display();
return 0;
} |
import java.util.ArrayList;
class BTreeNode {
// Vector of keys
private ArrayList<Integer> keys;
// Minimum degree
private int t;
// Vector of child pointers
private ArrayList<BTreeNode> C;
// Is true when node is leaf, else false
private boolean leaf;
// Constructor
public BTreeNode( int t, boolean leaf) {
this .t = t;
this .leaf = leaf;
this .keys = new ArrayList<>();
this .C = new ArrayList<>();
}
// Traversing the node and print its content
// with tab number of tabs before
public void traverse( int tab) {
StringBuilder s = new StringBuilder();
// Print 'tab' number of tabs
for ( int j = 0 ; j < tab; j++) {
s.append( '\t' );
}
for ( int i = 0 ; i < keys.size(); i++) {
// If this is not leaf, then before printing key[i]
// traverse the subtree rooted with child C[i]
if (!leaf) {
C.get(i).traverse(tab + 1 );
}
System.out.println(s.toString() + keys.get(i));
}
// Print the subtree rooted with the last child
if (!leaf) {
C.get(keys.size()).traverse(tab + 1 );
}
}
// Returns true if node is full
public boolean isFull() {
// returns true if node is full
return keys.size() == 2 * t - 1 ;
}
// Makes new root, setting current root as its child
public BTreeNode makeNewRoot( int val, BTreeNode newEntry) {
// Create new root
BTreeNode root = new BTreeNode(t, false );
// Stores keys value
root.keys.add(val);
// Push child pointers
root.C.add( this );
root.C.add(newEntry);
return root;
}
// Function to split the current node and store the new
// parent value in *val and new child pointer in &newEntry
// called only for splitting non-leaf node
public void split( int [] val, BTreeNode newEntry) {
// Create new non-leaf node
BTreeNode newNode = new BTreeNode(t, false );
// (t+1)th becomes parent
val[ 0 ] = this .keys.get(t);
// Last (t-1) entries will go to the new node
for ( int i = t + 1 ; i < 2 * t; i++) {
newNode.keys.add( this .keys.get(i));
}
// This node stores the first t entries
this .keys.subList(t, 2 * t - 1 ).clear();
// Last t entries will go to the new node
newNode.C.addAll( this .C.subList(t + 1 , 2 * t));
// This node stores the first (t+1) entries
this .C.subList(t + 1 , 2 * t + 1 ).clear();
this .C.add(newNode);
}
// Function to insert a new key in the given node.
// If the child of this node is split, we have to insert *val
// into keys vector and newEntry pointer into C vector
public void insert( int newKey, int [] val, BTreeNode[] newEntry) {
// Non-leaf node
if (!leaf) {
int i = 0 ;
// Find the first key greater than newKey
while (i < keys.size() && newKey > keys.get(i))
i++;
// We have to insert newKey into the left child of
// Node with index i
C.get(i).insert(newKey, val, newEntry);
// No split was done
if (newEntry[ 0 ] == null )
return ;
if (keys.size() < 2 * t - 1 ) {
// This node can accommodate a new key
// and child pointer entry
// Insert *val into key vector
keys.add(i, val[ 0 ]);
// Insert newEntry into C vector
C.add(i + 1 , newEntry[ 0 ]);
// As this node was not split, set newEntry
// to null
newEntry[ 0 ] = null ;
} else {
// Insert *val and newentry
keys.add(i, val[ 0 ]);
C.add(i + 1 , newEntry[ 0 ]);
// Current node has 2*t keys, so split it
split(val, newEntry[ 0 ]);
}
} else {
// Insert newKey in this node
int i = 0 ;
// Find correct position
while (i < keys.size() && newKey > keys.get(i))
i++;
// Insert in correct position
keys.add(i, newKey);
// If node is full
if (keys.size() == 2 * t) {
// Create a new node
BTreeNode newLeaf = new BTreeNode(t, true );
// Set (t+1)th key as parent
val[ 0 ] = this .keys.get(t);
// Insert last (t-1) keys into new node
newLeaf.keys.addAll(keys.subList(t + 1 , 2 * t));
// This node stores the first t keys
this .keys.subList(t, 2 * t - 1 ).clear();
newEntry[ 0 ] = newLeaf;
}
}
}
} class BTree {
// Root of B-Tree
private BTreeNode root;
// Minimum degree
private int t;
// Constructor
public BTree( int t) {
this .root = new BTreeNode(t, true );
this .t = t;
}
// Function to insert a node in the B-Tree
public void insert( int key) {
BTreeNode[] newEntry = { null };
int [] val = { 0 };
// Insert in B-Tree
root.insert(key, val, newEntry);
// If newEntry is not null then root needs to be
// split. Create new root
if (newEntry[ 0 ] != null ) {
root = root.makeNewRoot(val[ 0 ], newEntry[ 0 ]);
}
}
// Prints BTree
public void display() {
root.traverse( 0 );
}
} // Driver code public class Main {
public static void main(String[] args) {
// Create B-Tree
BTree tree = new BTree( 3 );
System.out.println( "After inserting 1 and 2" );
tree.insert( 1 );
tree.insert( 2 );
tree.display();
System.out.println( "After inserting 5 and 6" );
tree.insert( 5 );
tree.insert( 6 );
tree.display();
System.out.println( "After inserting 3 and 4" );
tree.insert( 3 );
tree.insert( 4 );
tree.display();
}
} |
# Python3 implementation for the above approach import bisect
class BTreeNode:
# The constructor method to initialize BTreeNode
def __init__( self , t, leaf):
self .keys = []
self .t = t
self .C = []
self .leaf = leaf
# Check if the node is full or not
def isFull( self ):
return len ( self .keys) = = ( 2 * self .t - 1 )
# Traverse the node
def traverse( self , tab):
s = "\t" * tab
if not self .leaf:
for i in range ( len ( self .keys)):
# Recursive call to traverse the child node
self .C[i].traverse(tab + 1 )
print (s + str ( self .keys[i]))
# Traverse the rightmost child node
self .C[i + 1 ].traverse(tab + 1 )
else :
# Print the leaf node
for i in range ( len ( self .keys)):
print (s + str ( self .keys[i]))
# Split the node
def split( self ):
newEntry = BTreeNode( self .t, self .leaf)
val = self .keys[ self .t - 1 ]
newEntry.keys = self .keys[ self .t:]
self .keys = self .keys[: self .t - 1 ]
if not self .leaf:
newEntry.C = self .C[ self .t:]
self .C = self .C[: self .t]
return val, newEntry
# Insert new key
def insert( self , new_key):
newEntry = None
if not self .leaf:
# Use bisect to find the index of new_key
i = bisect.bisect_left( self .keys, new_key)
# Recursively call insert on the child node
newEntry = self .C[i].insert(new_key)
# Check if new entry needs to be split
if newEntry is not None :
if len ( self .keys) < 2 * self .t - 1 :
self .keys.insert(i, newEntry[ 0 ])
self .C.insert(i + 1 , newEntry[ 1 ])
newEntry = None
else :
self .keys.insert(i, newEntry[ 0 ])
self .C.insert(i + 1 , newEntry[ 1 ])
val, newEntry = self .split()
else :
# Use bisect to find the index of new_key
i = bisect.bisect_left( self .keys, new_key)
# Insert the new_key
self .keys.insert(i, new_key)
# Check if the leaf node is full
if len ( self .keys) = = 2 * self .t - 1 :
newEntry = self .split()
return newEntry
# Create new root node
def makeNewRoot( self , val, newEntry):
root = BTreeNode( self .t, False )
root.keys.append(val)
root.C.append( self )
root.C.append(newEntry)
return root
class BTree:
# The constructor method to initialize BTree
def __init__( self , t):
self .root = BTreeNode(t, True )
self .t = t
# Insert new key
def insert( self , key):
newEntry = self .root.insert(key)
# Check if new entry needs to be split
if newEntry is not None :
self .root = self .root.makeNewRoot(newEntry[ 0 ], newEntry[ 1 ])
# Display the BTree
def display( self ):
self .root.traverse( 0 )
# Driver code tree = BTree( 2 )
print ( "After inserting 1 and 2" )
tree.insert( 1 )
tree.insert( 2 )
tree.display() print ( "After inserting 5 and 6" )
tree.insert( 5 )
tree.insert( 6 )
tree.display() print ( "After inserting 3 and 4" )
tree.insert( 3 )
tree.insert( 4 )
tree.display() # This code is contributed by amit_mangal_ |
using System;
using System.Collections.Generic;
public class BTreeNode
{ public List< int > keys = new List< int >();
public int t;
public List<BTreeNode> C = new List<BTreeNode>();
public bool leaf;
public BTreeNode( int t, bool leaf)
{
this .t = t;
this .leaf = leaf;
}
public bool isFull()
{
return keys.Count == (2 * t - 1);
}
public void traverse( int tab)
{
string s = new String( '\t' , tab);
if (!leaf)
{
for ( int i = 0; i < keys.Count; i++)
{
C[i].traverse(tab + 1);
Console.WriteLine(s + keys[i]);
}
C[keys.Count].traverse(tab + 1);
}
else
{
foreach ( int key in keys)
{
Console.WriteLine(s + key);
}
}
}
public Tuple< int , BTreeNode> split()
{
BTreeNode newEntry = new BTreeNode(t, leaf);
int val = keys[t - 1];
newEntry.keys.AddRange(keys.GetRange(t, keys.Count - t));
keys.RemoveRange(t - 1, keys.Count - t + 1);
if (!leaf)
{
newEntry.C.AddRange(C.GetRange(t, C.Count - t));
C.RemoveRange(t, C.Count - t);
}
return Tuple.Create(val, newEntry);
}
public Tuple< int , BTreeNode> insert( int new_key)
{
Tuple< int , BTreeNode> newEntry = null ;
if (!leaf)
{
int i = keys.BinarySearch(new_key);
if (i < 0) i = ~i;
newEntry = C[i].insert(new_key);
if (newEntry != null )
{
if (keys.Count < 2 * t - 1)
{
keys.Insert(i, newEntry.Item1);
C.Insert(i + 1, newEntry.Item2);
newEntry = null ;
}
else
{
keys.Insert(i, newEntry.Item1);
C.Insert(i + 1, newEntry.Item2);
newEntry = split();
}
}
}
else
{
int i = keys.BinarySearch(new_key);
if (i < 0) i = ~i;
keys.Insert(i, new_key);
if (keys.Count == 2 * t - 1)
{
newEntry = split();
}
}
return newEntry;
}
public BTreeNode makeNewRoot( int val, BTreeNode newEntry)
{
BTreeNode root = new BTreeNode(t, false );
root.keys.Add(val);
root.C.Add( this );
root.C.Add(newEntry);
return root;
}
} public class BTree
{ public BTreeNode root;
public int t;
public BTree( int t)
{
this .root = new BTreeNode(t, true );
this .t = t;
}
public void insert( int key)
{
Tuple< int , BTreeNode> newEntry = root.insert(key);
if (newEntry != null )
{
root = root.makeNewRoot(newEntry.Item1, newEntry.Item2);
}
}
public void display()
{
root.traverse(0);
}
} class Program
{ static void Main( string [] args)
{
BTree tree = new BTree(2);
Console.WriteLine( "After inserting 1 and 2" );
tree.insert(1);
tree.insert(2);
tree.display();
Console.WriteLine( "After inserting 5 and 6" );
tree.insert(5);
tree.insert(6);
tree.display();
Console.WriteLine( "After inserting 3 and 4" );
tree.insert(3);
tree.insert(4);
tree.display();
}
} |
class BTreeNode { constructor(t, leaf) {
this .keys = [];
this .t = t;
this .C = [];
this .leaf = leaf;
}
isFull() {
return this .keys.length === 2 * this .t - 1;
}
traverse(tab) {
const s = "\t" .repeat(tab);
if (! this .leaf) {
for (let i = 0; i < this .keys.length; i++) {
this .C[i].traverse(tab + 1);
console.log(s + this .keys[i]);
}
this .C[ this .keys.length].traverse(tab + 1);
} else {
for (let i = 0; i < this .keys.length; i++) {
console.log(s + this .keys[i]);
}
}
}
split() {
const newEntry = new BTreeNode( this .t, this .leaf);
const val = this .keys[ this .t - 1];
newEntry.keys = this .keys.slice( this .t);
this .keys = this .keys.slice(0, this .t - 1);
if (! this .leaf) {
newEntry.C = this .C.slice( this .t);
this .C = this .C.slice(0, this .t);
}
return [val, newEntry];
}
// Insert a new key into the B-tree
insert(newKey) {
let newEntry = null ;
if (! this .leaf) {
// Use binary search to find the index for insertion
const i = this .binarySearch(newKey);
// Recursively call insert on the child node
newEntry = this .C[i].insert(newKey);
// Check if a new entry needs to be split
if (newEntry !== null ) {
if ( this .keys.length < 2 * this .t - 1) {
// Insert the new entry into the current node
this .keys.splice(i, 0, newEntry[0]);
this .C.splice(i + 1, 0, newEntry[1]);
newEntry = null ;
} else {
// Split the current node and insert the new entry
this .keys.splice(i, 0, newEntry[0]);
this .C.splice(i + 1, 0, newEntry[1]);
[newKey, newEntry] = this .split();
}
}
} else {
// Use binary search to find the index for insertion
const i = this .binarySearch(newKey);
// Insert the new key into the leaf node
this .keys.splice(i, 0, newKey);
// Check if the leaf node is full and needs to be split
if ( this .keys.length === 2 * this .t - 1) {
newEntry = this .split();
}
}
return newEntry;
}
// Perform binary search to find the index for insertion
binarySearch(newKey) {
let left = 0;
let right = this .keys.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if ( this .keys[mid] === newKey) {
return mid;
} else if ( this .keys[mid] < newKey) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
// Create a new root node
makeNewRoot(val, newEntry) {
const root = new BTreeNode( this .t, false );
root.keys.push(val);
root.C.push( this );
root.C.push(newEntry);
return root;
}
} class BTree { constructor(t) {
this .root = new BTreeNode(t, true );
this .t = t;
}
// Insert a new key into the B-tree
insert(key) {
const newEntry = this .root.insert(key);
// Check if a new entry needs to be split and create a new root if necessary
if (newEntry !== null ) {
this .root = this .root.makeNewRoot(newEntry[0], newEntry[1]);
}
}
// Display the B-tree
display() {
this .root.traverse(0);
}
} // Driver code const tree = new BTree(2);
console.log( "After inserting 1 and 2" );
tree.insert(1); tree.insert(2); tree.display(); console.log( "After inserting 5 and 6" );
tree.insert(5); tree.insert(6); tree.display(); console.log( "After inserting 3 and 4" );
tree.insert(3); tree.insert(4); tree.display(); |
After inserting 1 and 2 1 2 After inserting 5 and 6 1 2 5 6 After inserting 3 and 4 1 2 3 4 5 6