B 树和 B+ 树

一、B 树和 B+ 树的基本概念

B 树和 B+ 树是两种重要的平衡多路搜索树,特别适合在磁盘等外存设备上组织和存储数据。它们通过增加节点的分支因子,减少树的高度,从而减少磁盘 I/O 操作,提高数据访问效率。

B 树 (B-Tree)

B 树是一种自平衡的多路搜索树,每个节点可以有多个子节点。

主要特点
  1. 所有叶子节点在同一层
  2. 每个节点可以包含多个键值和子节点
  3. 节点的键值按升序排列
  4. 除根节点外,每个节点至少有 m/2 个子节点 (m 为树的阶数)
  5. 每个节点最多有 m 个子节点
B 树的结构
class BTreeNode {
  constructor(isLeaf = false) {
    this.isLeaf = isLeaf;
    this.keys = [];
    this.children = [];
  }
}

class BTree {
  constructor(minDegree) {
    this.root = new BTreeNode(true);
    this.minDegree = minDegree; // 最小度,决定节点的最小键数和子节点数
  }

  // 搜索键
  search(key, node = this.root) {
    let i = 0;
    while (i < node.keys.length && key > node.keys[i]) {
      i++;
    }
    
    if (i < node.keys.length && key === node.keys[i]) {
      return node;
    }
    
    if (node.isLeaf) {
      return null;
    }
    
    return this.search(key, node.children[i]);
  }

  // 插入键
  insert(key) {
    const root = this.root;
    
    if (root.keys.length === (2 * this.minDegree - 1)) {
      const newRoot = new BTreeNode();
      this.root = newRoot;
      newRoot.children.push(root);
      this.splitChild(newRoot, 0);
      this.insertNonFull(newRoot, key);
    } else {
      this.insertNonFull(root, key);
    }
  }

  // 在非满节点插入键
  insertNonFull(node, key) {
    let i = node.keys.length - 1;
    
    if (node.isLeaf) {
      node.keys.push(null);
      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--;
      }
      i++;
      
      if (node.children[i].keys.length === (2 * this.minDegree - 1)) {
        this.splitChild(node, i);
        if (key > node.keys[i]) {
          i++;
        }
      }
      
      this.insertNonFull(node.children[i], key);
    }
  }

  // 分裂子节点
  splitChild(parentNode, index) {
    const minDegree = this.minDegree;
    const childNode = parentNode.children[index];
    const newNode = new BTreeNode(childNode.isLeaf);
    
    parentNode.children.splice(index + 1, 0, newNode);
    parentNode.keys.splice(index, 0, childNode.keys[minDegree - 1]);
    
    newNode.keys = childNode.keys.slice(minDegree);
    childNode.keys = childNode.keys.slice(0, minDegree - 1);
    
    if (!childNode.isLeaf) {
      newNode.children = childNode.children.slice(minDegree);
      childNode.children = childNode.children.slice(0, minDegree);
    }
  }

  // 打印B树
  printTree(node = this.root, level = 0) {
    let indent = '  '.repeat(level);
    console.log(indent + 'Level ' + level + ': ' + node.keys.join(', '));
    
    if (!node.isLeaf) {
      for (let i = 0; i < node.children.length; i++) {
        this.printTree(node.children[i], level + 1);
      }
    }
  }
}

// 使用示例
const bTree = new BTree(3); // 创建一个最小度为3的B树
bTree.insert(10);
bTree.insert(20);
bTree.insert(5);
bTree.insert(6);
bTree.insert(12);
bTree.insert(30);
bTree.insert(7);
bTree.insert(17);

console.log("B树结构:");
bTree.printTree();

B+ 树 (B+Tree)

B+ 树是 B 树的一种变体,它在数据库和文件系统中被广泛使用。

主要特点
  1. 所有数据都存储在叶子节点,非叶子节点只存储索引信息
  2. 叶子节点之间通过指针相连,形成有序链表
  3. 叶子节点包含所有键值和对应的数据记录
  4. 非叶子节点的键值是其子节点中最大键值的副本
  5. 与 B 树相比,B+ 树的查询效率更加稳定
B+ 树的结构
class BPlusTreeNode {
  constructor(isLeaf = false) {
    this.isLeaf = isLeaf;
    this.keys = [];
    this.children = [];
    this.next = null; // 用于叶子节点之间的链接
  }
}
class BPlusTree {
  constructor(minDegree) {
    this.root = new BPlusTreeNode(true);
    this.minDegree = minDegree;
  }
  // 搜索键
  search(key) {
    let node = this.root;
    
    while (!node.isLeaf) {
      let i = 0;
      while (i < node.keys.length && key > node.keys[i]) {
        i++;
      }
      node = node.children[i];
    }    
    // 在叶子节点中查找
    for (let i = 0; i < node.keys.length; i++) {
      if (node.keys[i] === key) {
        return true;
      }
    }    
    return false;
  }
  // 插入键
  insert(key) {
    const root = this.root;    
    if (root.keys.length === (2 * this.minDegree - 1)) {
      const newRoot = new BPlusTreeNode();
      this.root = newRoot;
      newRoot.children.push(root);      
      this.splitChild(newRoot, 0);
      this.insertNonFull(newRoot, key);
    } else {
      this.insertNonFull(root, key);
    }
  }
  // 在非满节点插入键
  insertNonFull(node, key) {
    let i = node.keys.length - 1;
    
    if (node.isLeaf) {
      node.keys.push(null);
      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--;
      }
      i++;      
      if (node.children[i].keys.length === (2 * this.minDegree - 1)) {
        this.splitChild(node, i);
        if (key > node.keys[i]) {
          i++;
        }
      }      
      this.insertNonFull(node.children[i], key);
    }
  }

  // 分裂子节点
  splitChild(parentNode, index) {
    const minDegree = this.minDegree;
    const childNode = parentNode.children[index];
    const newNode = new BPlusTreeNode(childNode.isLeaf);    
    parentNode.children.splice(index + 1, 0, newNode);
    parentNode.keys.splice(index, 0, childNode.keys[minDegree - 1]);
    
    newNode.keys = childNode.keys.slice(minDegree);
    childNode.keys = childNode.keys.slice(0, minDegree - 1);
    
    if (!childNode.isLeaf) {
      newNode.children = childNode.children.slice(minDegree);
      childNode.children = childNode.children.slice(0, minDegree);
    } else {
      // 处理叶子节点之间的链接
      newNode.next = childNode.next;
      childNode.next = newNode;
    }
  }

  // 范围查询
  rangeQuery(start, end) {
    const result = [];
    let node = this.root;
    
    // 找到起始键所在的叶子节点
    while (!node.isLeaf) {
      let i = 0;
      while (i < node.keys.length && start > node.keys[i]) {
        i++;
      }
      node = node.children[i];
    }
    
    // 遍历叶子节点链表,收集范围内的键
    while (node) {
      for (let key of node.keys) {
        if (key >= start && key <= end) {
          result.push(key);
        }
        if (key > end) {
          return result;
        }
      }
      node = node.next;
    }   
    return result;
  }
  // 打印B+树
  printTree() {
    let node = this.root;
    let level = 0;
    
    while (!node.isLeaf) {
      console.log(`Level ${level}: ${node.keys.join(', ')}`);
      node = node.children[0];
      level++;
    }
    // 打印叶子节点
    console.log(`Level ${level} (Leaf):`);
    let leafNode = node;
    let leafLevel = [];
    while (leafNode) {
      leafLevel.push(...leafNode.keys);
      leafNode = leafNode.next;
    }
    console.log(leafLevel.join(', '));
  }
}
// 使用示例
const bPlusTree = new BPlusTree(3);
bPlusTree.insert(10);
bPlusTree.insert(20);
bPlusTree.insert(5);
bPlusTree.insert(6);
bPlusTree.insert(12);
bPlusTree.insert(30);
bPlusTree.insert(7);
bPlusTree.insert(17);
console.log("B+树结构:");
bPlusTree.printTree();
console.log("范围查询 (5-15):", bPlusTree.rangeQuery(5, 15));

B 树和 B+ 树的主要区别

1. 数据存储位置:
  • B 树:键值和数据都存储在所有节点中
  • B+ 树:只有叶子节点存储数据,非叶子节点只存储索引
2. 搜索效率:
  • B 树:搜索可能在任何节点结束
  • B+ 树:搜索必须到达叶子节点才能结束,效率更稳定
3. 范围查询:
  • B 树:需要中序遍历整个树
  • B+ 树:通过叶子节点的链表可以高效地进行范围查询
4. 空间利用:
  • B 树:每个节点都存储数据,空间利用率较高
  • B+ 树:非叶子节点不存储数据,空间利用率相对较低
5. 应用场景:
  • B 树:适用于随机访问较多的场景
  • B+ 树:适用于范围查询较多的场景,如数据库索引
B 树和 B+ 树的应用
1. 数据库系统:
  • MySQL、Oracle 等关系型数据库的索引结构通常使用 B+ 树
  • MongoDB 等 NoSQL 数据库也使用 B 树或 B+ 树作为索引结构
2. 文件系统:
  • NTFS、ext4 等文件系统使用 B+ 树来组织文件索引
  • B+ 树的结构非常适合文件系统中的范围查询
3. 内存数据库:
  • Redis 等内存数据库在需要持久化时也会使用 B 树或 B + 树
4. 搜索引擎:
  • 搜索引擎的索引结构也会使用 B 树或 B+ 树来提高查询效率

B 树和 B+ 树是现代计算机系统中非常重要的数据结构,它们通过平衡多路搜索的特性,有效地减少了磁盘 I/O 操作,提高了数据访问效率,是数据库和文件系统等应用的核心组件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值