【JavaScript数据结构】B树 & B+树

95 阅读2分钟

B树和B+树是在大型存储系统中,如文件系统和数据库中使用的平衡搜索树。由于它们的实现相对复杂,前端一般不会涉及。

1. B树

1.1 B树的基本属性

  • B树的每个节点可能包含多个子节点。
  • B树的所有叶子节点在同一层。
  • 每个节点包含n个元素,按升序排列,并且有n+1个子节点。

1.2 B树的实现

定义B树节点:

class BTreeNode {
    constructor(t) {
        this.keys = [];        // 存储关键字
        this.children = [];   // 子节点
        this.leaf = true;     // 是否是叶子节点
        this.t = t;           // B树的度
    }
}

class BTree {
    constructor(t) {
        this.root = new BTreeNode(t);
        this.t = t;
    }
}

// 在B树中查找一个给定的关键字
BTree.prototype.search = function(node, k) {
    let i = 0;
    while (i < node.n && k > node.key[i]) {
        i++;
    }
    if (i < node.n && k == node.key[i]) {
        return [node, i];
    } else if (node.leaf) {
        return null;
    } else {
        return this.search(node.child[i], k);
    }
}

// 插入一个关键字到B树中
BTree.prototype.insert = function(k) {
    let r = this.root;
    if (r.n == 2*this.t - 1) {
        let s = new BTreeNode(this.t, false);
        this.root = s;
        s.child[0] = r;
        this.splitChild(s, 0);
        this.insertNonFull(s, k);
    } else {
        this.insertNonFull(r, k);
    }
}

BTree.prototype.splitChild = function(x, i) {
    let t = this.t;
    let z = new BTreeNode(t);
    let y = x.child[i];
    z.leaf = y.leaf;
    z.n = t - 1;

    for (let j = 0; j < t - 1; j++) {
        z.key[j] = y.key[j+t];
    }
    if (!y.leaf) {
        for (let j = 0; j < t; j++) {
            z.child[j] = y.child[j+t];
        }
    }
    y.n = t - 1;

    for (let j = x.n; j >= i+1; j--) {
        x.child[j+1] = x.child[j];
    }
    x.child[i+1] = z;

    for (let j = x.n - 1; j >= i; j--) {
        x.key[j+1] = x.key[j];
    }
    x.key[i] = y.key[t-1];
    x.n = x.n + 1;
}

BTree.prototype.insertNonFull = function(x, k) {
    let i = x.n - 1;
    if (x.leaf) {
        while (i >= 0 && k < x.key[i]) {
            x.key[i+1] = x.key[i];
            i--;
        }
        x.key[i+1] = k;
        x.n = x.n + 1;
    } else {
        while (i >= 0 && k < x.key[i]) {
            i--;
        }
        i++;
        if (x.child[i].n == 2*this.t - 1) {
            this.splitChild(x, i);
            if (k > x.key[i]) {
                i++;
            }
        }
        this.insertNonFull(x.child[i], k);
    }
}


2. B+树

B+树与B树类似,但它有以下特点:

  1. B+树的所有值都出现在叶子节点中。
  2. B+树的非叶子节点只用于指示路径,所以它包含了更多的分支因子。
  3. B+树的所有叶子节点都是通过一个链表连接的,这对于范围查询特别有用。
class BPlusTreeNode {
    constructor(t) {
        this.keys = [];        // 存储关键字
        this.children = [];   // 子节点或者实际记录
        this.leaf = true;     // 是否是叶子节点
        this.nextSibling = null; // 下一个兄弟节点
        this.t = t;           // B+树的度
    }
}

class BPlusTree {
    constructor(t) {
        this.root = new BPlusTreeNode(t);
        this.t = t;
        this.head = null;     // 指向第一个叶子节点的指针
    }
    ...
}