Mastering B-Trees in TypeScript: A Comprehensive Guide to Balanced Search Trees
In this guide, we’ll walk through the process of implementing a B-tree in TypeScript, using classes and recursion to manage the structure and operations of this balanced tree. We'll explain each step, set up a console-based testing environment, and explore practical use cases where B-trees are beneficial.
What is a B-Tree?
A B-tree is a self-balancing search tree commonly used in databases and file systems for efficient data management. Unlike binary search trees, B-trees allow nodes to contain multiple keys and have multiple children, ensuring the tree remains balanced and minimizing disk reads.
Key Properties of a B-Tree:
Multiple Keys: Each node holds several keys, kept in sorted order.
Multiple Children: Each node can have multiple children, organized by key ranges.
Balanced Depth: All leaf nodes are at the same depth, making B-trees balanced.
Efficient Access: Data can be accessed quickly with minimal disk I/O.
Step 1: Defining B-Tree Classes in TypeScript
To start, let’s define the BTreeNode
and BTree
classes. The BTreeNode
class represents individual nodes in the tree, while the BTree
class manages the tree structure and operations like inserting and searching.
// BTree.ts
class BTreeNode {
keys: number[];
children: BTreeNode[];
isLeaf: boolean;
constructor(public degree: number, isLeaf: boolean) {
this.keys = [];
this.children = [];
this.isLeaf = isLeaf;
}
// Splits the child node to handle node overflows
splitChild(i: number, y: BTreeNode) {
const z = new BTreeNode(y.degree, y.isLeaf);
z.keys = y.keys.splice(this.degree - 1, this.degree - 1);
if (!y.isLeaf) {
z.children = y.children.splice(this.degree);
}
this.children.splice(i + 1, 0, z);
this.keys.splice(i, 0, y.keys.pop() as number);
}
// Inserts a key into a non-full node
insertNonFull(key: number) {
let i = this.keys.length - 1;
if (this.isLeaf) {
while (i >= 0 && this.keys[i] > key) {
i--;
}
this.keys.splice(i + 1, 0, key);
} else {
while (i >= 0 && this.keys[i] > key) {
i--;
}
i++;
if (this.children[i].keys.length === 2 * this.degree - 1) {
this.splitChild(i, this.children[i]);
if (this.keys[i] < key) {
i++;
}
}
this.children[i].insertNonFull(key);
}
}
// Searches for a key in the subtree rooted at this node
search(key: number): boolean {
let i = 0;
while (i < this.keys.length && key > this.keys[i]) {
i++;
}
if (this.keys[i] === key) {
return true;
}
return this.isLeaf ? false : this.children[i].search(key);
}
}
class BTree {
root: BTreeNode | null = null;
constructor(public degree: number) {}
// Searches for a key in the B-tree
find(key: number): boolean {
return this.root === null ? false : this.root.search(key);
}
// Inserts a key into the B-tree
insert(key: number) {
if (this.root === null) {
this.root = new BTreeNode(this.degree, true);
this.root.keys.push(key);
} else {
if (this.root.keys.length === 2 * this.degree - 1) {
const s = new BTreeNode(this.degree, false);
s.children.push(this.root);
s.splitChild(0, this.root);
this.root = s;
}
this.root.insertNonFull(key);
}
}
}
export { BTree };
Step 2: Testing the B-Tree in a Console Environment
To test our B-tree, we’ll set up a TypeScript project and use console output to observe the tree’s behavior as we insert and search for keys.
1. Project Setup
Create a folder for the project, initialize it with TypeScript, and install necessary dependencies:
mkdir BTreeExample
cd BTreeExample
npm init -y
npm install typescript --save-dev
npx tsc --init
2. Add B-Tree Code and Testing Script
Add the above B-tree code to a file named BTree.ts
. Then, create another file called index.ts
for inserting and finding values in the tree.
// index.ts
import { BTree } from './BTree';
// Create a new BTree instance with a degree of 2
const bTree = new BTree(2);
// Insert values into the B-tree
bTree.insert(10);
bTree.insert(20);
bTree.insert(5);
bTree.insert(6);
bTree.insert(12);
bTree.insert(30);
bTree.insert(7);
bTree.insert(17);
// Search for values and log results to the console
console.log("Find 6:", bTree.find(6)); // Output: true
console.log("Find 15:", bTree.find(15)); // Output: false
console.log("Find 10:", bTree.find(10)); // Output: true
3. Compile and Run
Compile and execute the code to see the output in the console:
npx tsc
node dist/index.js
Expected Output
The console output should look like this:
Find 6: true
Find 15: false
Find 10: true
Where Are B-Trees Used?
B-trees are widely used in systems requiring efficient search and insert operations over large datasets, including:
Databases: B-trees efficiently manage indexes in databases, allowing quick lookups while keeping the structure balanced.
File Systems: Many file systems, such as NTFS, use B-trees to index large directories.
Operating Systems: B-trees also play a role in organizing data for quick retrieval within certain operating system components.
This guide covers the basics of B-tree creation and testing in TypeScript, and you can explore the code further on our StackBlitz (to be forked) for interactive examples and experimentation.
With these tools, you're equipped to apply B-trees for efficient data management and gain a deeper understanding of balanced search trees in action. Happy coding!
Subscribe to my newsletter
Read articles from Pawan Gangwani directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Pawan Gangwani
Pawan Gangwani
I’m Pawan Gangwani, a passionate Full Stack Developer with over 12 years of experience in web development. Currently serving as a Lead Software Engineer at Lowes India, I specialize in modern web applications, particularly in React and performance optimization. I’m dedicated to best practices in coding, testing, and Agile methodologies. Outside of work, I enjoy table tennis, exploring new cuisines, and spending quality time with my family.