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 操作,提高了数据访问效率,是数据库和文件系统等应用的核心组件。

### B的阶数定义与计算方法 #### 阶数的定义 B的阶数是指中节点的最大子节点个数。例如,在2-3中,每个节点最多有三个子节点,因此它的阶数为3;而在2-3-4中,每个节点最多有四个子节点,则其阶数为4[^3]。 此外,对于m阶B,“m”表示该的阶数或称为度数。不同资料可能会有不同的称呼方式,但在本文中统一定义为“阶”。这意味着当提到某个B为m阶时,意味着此中的任何内部节点至多具有m个孩子节点[^2]。 #### 计算高度的方法 精确地计算一棵给定B的高度往往需要遍历整个结构,这是一项耗费资源的操作。然而,在实际应用场景下,可以通过一些简化手段来进行估算。一种常用的技术是基于存储的数据总量以及指定的阶数值运用对数函数得出近似的高度值[^1]。这种方法不仅有助于理解系统的整体表现情况,而且还能辅助完成诸如内存分配之类的前期准备工作。 假设N代表总的键数目,m代表每节点最大能容纳的关键字数量(即阶减一),那么理论上最小可能达到的高度H可通过下面公式得到: \[ H = \lceil\log_{\text{min degree}}(\frac{\text{number of keys} + 1}{2})\rceil \] 其中 min degree 表示最低限度允许的孩子节点数(通常是 ⌈m / 2⌉),这样可以提供关于如何构建高效索引机制的重要线索。 以下是实现上述逻辑的一个简单Python例子: ```python import math def calculate_btree_height(num_keys, order): """ Calculate the minimum height of a B-tree given number of keys and its order. Parameters: num_keys (int): Total number of keys stored in the tree. order (int): The maximum number of children an internal node can have. Returns: int: Minimum possible height of the b-tree. """ if num_keys == 0 or order <= 1: return 0 # Compute minimal degree which is ceil(order / 2) min_degree = math.ceil(order / 2) # Apply formula to get lower bound on height numerator = (num_keys + 1) / 2 height = math.ceil(math.log(numerator, min_degree)) return height # Example usage: print(calculate_btree_height(15, 5)) # Output should be around expected theoretical value based upon inputs provided here. ``` 通过以上讨论可以看出,掌握好这些基本原理可以帮助开发者更好地设计数据库管理系统或者文件系统等相关软件组件。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值