B-Trees are a type of self-balancing tree data structure that maintains sorted data and allows searches, sequential access, insertions, and deletions in logarithmic time. They are particularly useful for storage systems like databases and file systems because they can efficiently handle large amounts of data stored on disk.
t and 2t-1.A B-tree node consists of:
t-1 to 2t-2 keys.t to 2t child pointers.The search operation in a B-tree is similar to binary search in a binary tree but adapted for multiple keys per node.
function search(node, key) {
let i = 0;
while (i < node.keys.length && key > node.keys[i]) {
i++;
}
if (node.leaf) {
return i < node.keys.length && node.keys[i] === key ? node : null;
} else {
return search(node.children[i], key);
}
}
Insertion in a B-tree involves finding the correct position for the new key and inserting it. If a node overflows (more than 2t-1 keys), it is split into two nodes.
function insertNonFull(node, key) {
let i = node.keys.length - 1;
if (node.leaf) {
while (i >= 0 && key < node.keys[i]) {
node.keys[i + 1] = node.keys[i];
i--;
}
node.keys[i + 1] = key;
} else {
while (i >= 0 && key < node.keys[i]) {
i++;
}
if (node.children[i].keys.length === 2 * t - 1) {
splitChild(node, i);
if (key > node.keys[i]) {
i++;
}
}
insertNonFull(node.children[i], key);
}
}
function splitChild(parent, index) {
const child = parent.children[index];
const newChild = new BTreeNode(t, true);
for (let j = 0; j < t - 1; j++) {
newChild.keys[j] = child.keys[j + t];
}
if (!child.leaf) {
for (let j = 0; j < t; j++) {
newChild.children[j] = child.children[j + t];
}
}
child.keys.length = t - 1;
parent.children.splice(index + 1, 0, newChild);
for (let j = parent.keys.length - 1; j >= index; j--) {
parent.keys[j + 1] = parent.keys[j];
}
parent.keys[index] = child.keys[t - 1];
}
Deletion in a B-tree is more complex due to the need to maintain the minimum degree t. If a node underflows (fewer than t-1 keys), it may be merged with a sibling or borrow a key from one.
function deleteKey(node, key) {
let index = 0;
while (index < node.keys.length && key > node.keys[index]) {
index++;
}
if (node.leaf) {
if (index < node.keys.length && node.keys[index] === key) {
node.keys.splice(index, 1);
}
} else {
if (index < node.keys.length && node.keys[index] === key) {
if (node.children[index].keys.length >= t) {
let pred = getPred(node, index);
node.keys[index] = pred;
deleteKey(node.children[index], pred);
} else if (node.children[index + 1].keys.length >= t) {
let succ = getSucc(node, index);
node.keys[index] = succ;
deleteKey(node.children[index + 1], succ);
} else {
merge(node, index);
deleteKey(node.children[index], key);
}
} else {
if (node.children[index].keys.length < t) {
fill(index);
}
if (index > node.keys.length && node.children[index - 1]) {
deleteKey(node.children[index - 1], key);
} else {
deleteKey(node.children[index], key);
}
}
}
}
t: The value of t affects the height and width of the tree. A larger t results in fewer levels but more keys per node, which can be beneficial for disk-based storage.B-trees are a powerful data structure for managing large datasets efficiently. Their ability to maintain balance and handle multiple keys per node makes them ideal for applications requiring fast access, insertion, and deletion operations on disk-based storage systems. Understanding B-trees is crucial for developers working with databases and file systems.