二叉树与B树
二叉树是一种数据结构,它的每个节点最多有两个子节点,分别称为左子节点和右子节点。二叉树可以用来实现排序、查找、遍历等操作。
B树是一种平衡的多路查找树,它的每个节点可以有多个子节点,通常与磁盘或其他外部存储器相关联。B树的目的是减少磁盘的I/O操作,提高查找效率。
二叉树和B树的区别
- 二叉树是一种典型的普通树,而B树是一种中序遍历结果有序的多路平衡树。
- 二叉树中的节点最多可以有两个子节点,而B树中的节点可以有多个子节点,取决于磁盘块的大小。
- 二叉树的高度为 log_ {2} N ,其中 N 是节点个数。而B树的高度为 log_ {M} N ,其中 M 是B树的阶,也就是一个节点可以最多包含关键字的个数。
- 二叉树中节点的插入和删除操作相对简单,而B树中节点的插入和删除操作相对复杂,需要进行分裂、合并、旋转等操作来保持平衡。
B树代码
// B树的节点类
class BTreeNode {
int[] keys; // 存储关键字的数组
int t; // 每个节点最少有t-1个关键字,最多有2t-1个关键字
BTreeNode[] children; // 存储子节点的数组
int n; // 当前节点的关键字个数
boolean leaf; // 是否是叶子节点
// 构造函数,初始化节点
public BTreeNode(int t, boolean leaf) {
this.t = t;
this.leaf = leaf;
this.keys = new int[2 * t - 1]; // 分配空间给关键字数组
this.children = new BTreeNode[2 * t]; // 分配空间给子节点数组
this.n = 0; // 初始化关键字个数为0
}
// 打印节点及其子树的内容,用于调试
public void print() {
System.out.print("[");
for (int i = 0; i < n; i++) {
if (!leaf) {
children[i].print(); // 递归打印子节点
}
System.out.print(" " + keys[i] + " ");
}
if (!leaf) {
children[n].print(); // 递归打印最后一个子节点
}
System.out.print("]");
}
// 在不满的节点中插入一个关键字,假设该节点不是叶子节点
public void insertNonFull(int k) {
int i = n - 1; // 从右向左找到插入位置
if (leaf) { // 如果是叶子节点,直接插入并排序
while (i >= 0 && keys[i] > k) {
keys[i + 1] = keys[i]; // 将大于k的关键字右移一位
i--;
}
keys[i + 1] = k; // 将k插入到合适的位置
n++; // 增加关键字个数
} else { // 如果不是叶子节点,需要找到合适的子节点进行插入
while (i >= 0 && keys[i] > k) {
i--; // 找到第一个小于等于k的关键字
}
i++; // i是要插入的子节点的索引
if (children[i].n == 2 * t - 1) { // 如果该子节点已满,需要分裂
splitChild(i); // 分裂该子节点,将中间的关键字上移至当前节点
if (keys[i] < k) { // 判断k应该插入到哪个分裂后的子节点中
i++;
}
}
children[i].insertNonFull(k); // 递归地在合适的子节点中插入k
}
}
// 分裂一个满的子节点,将中间的关键字上移至当前节点中,假设当前节点不满
public void splitChild(int i) {
BTreeNode y = children[i]; // y是要分裂的子节点
BTreeNode z = new BTreeNode(y.t, y.leaf); // z是分裂后的新节点,和y有相同的t和leaf属性
z.n = t - 1; // z有t-1个关键字
for (int j = 0; j < t - 1; j++) { // 将y的后t-1个关键字复制到z中
z.keys[j] = y.keys[j + t];
}
if (!y.leaf) { // 如果y不是叶子节点,还需要将y的后t个子节点复制到z中
for (int j = 0; j < t; j++) { // 将y的后t个子节点复制到z中
z.children[j] = y.children[j + t];
}
}
y.n = t - 1; // y现在只有t-1个关键字
for (int j = n; j >= i + 1; j--) { // 将当前节点的子节点数组右移一位,为z腾出空间
children[j + 1] = children[j];
}
children[i + 1] = z; // 将z作为当前节点的子节点
for (int j = n - 1; j >= i; j--) { // 将当前节点的关键字数组右移一位,为y的中间关键字腾出空间
keys[j + 1] = keys[j];
}
keys[i] = y.keys[t - 1]; // 将y的中间关键字上移至当前节点
n++; // 增加当前节点的关键字个数
}
// 在B树中查找一个关键字,返回一个包含该关键字的节点和索引的对象,如果不存在则返回null
public SearchResult search(int k) {
int i = 0; // 从左向右找到第一个大于等于k的关键字
while (i < n && keys[i] < k) {
i++;
}
if (i < n && keys[i] == k) { // 如果找到了k,返回结果
return new SearchResult(this, i);
}
if (leaf) { // 如果是叶子节点,说明k不存在,返回null
return null;
} else { // 如果不是叶子节点,递归地在合适的子节点中查找k
return children[i].search(k);
}
}
}
// B树的类
class BTree {
BTreeNode root; // 根节点
int t; // 每个节点最少有t-1个关键字,最多有2t-1个关键字
// 构造函数,初始化B树
public BTree(int t) {
this.t = t;
this.root = null;
}
// 打印B树的内容,用于调试
public void print() {
if (root != null) {
root.print();
}
}
// 在B树中插入一个关键字,如果已存在则不插入
public void insert(int k) {
if (root == null) { // 如果根节点为空,创建一个新的根节点并插入k
root = new BTreeNode(t, true);
root.keys[0] = k;
root.n = 1;
} else { // 如果根节点不为空,先检查是否已满
if (root.n == 2 * t - 1) { // 如果根节点已满,需要分裂并增加高度
BTreeNode s = new BTreeNode(t, false); // s是新的根节点
s.children[0] = root; // s的第一个子节点是原来的根节点
s.splitChild(0); // 分裂原来的根节点,将中间的关键字上移至s中
int i = 0; // 判断k应该插入到哪个分裂后的子节点中
if (s.keys[0] < k) {
i++;
}
s.children[i].insertNonFull(k); // 在合适的子节点中插入k
root = s; // 更新根节点为s
} else { // 如果根节点不满,直接在根节点中插入k
root.insertNonFull(k);
}
}
}
// 在B树中查找一个关键字,返回一个包含该关键字的节点和索引的对象,如果不存在则返回null
public SearchResult search(int k) {
if (root == null) { // 如果根节点为空,说明B树为空,返回null
return null;
} else { // 如果根节点不为空,递归地在根节点中查找k
return root.search(k);
}
}
}
// 查找结果的类,包含一个节点和一个索引
class SearchResult {
BTreeNode node; // 包含关键字的节点
int index; // 关键字在节点中的索引
// 构造函数,初始化查找结果
public SearchResult(BTreeNode node, int index) {
this.node = node;
this.index = index;
}
}
多路查找树
B 树、B+树和 B*树
多路查找树是一种特殊的查找树,它的每个节点可以有多个元素和多个子节点,从而提高了查找效率。多路查找树有不同的类型,如2-3树、2-3-4树、B树、B+树和B*树,它们的区别主要在于节点的元素个数和子节点个数,以及元素在节点中的分布方式
概念:
- m叉树:每个非叶节点有最多m棵子树的树,其中m称为树的度。
- 2-3树:一种特殊的m叉树,每个节点最多有两个子节点或三个子节点,且所有叶节点在同一层上。
- 2-3-4树:一种特殊的m叉树,每个节点最多有两个子节点或三个子节点或四个子节点,且所有叶节点在同一层上。
- B树:一种平衡的m叉树,用于磁盘或其他随机访问的存储设备上的数据结构。B树通常具有大的度数和大的高度。
- B+树:B树的一种变体,它将数据存储在叶子节点中,而非内部节点中,以提高数据访问的效率。
- B*树:B+树的一种变体,它将内部节点占据的空间比例增加到了2/3,以降低B+树的高度。
- 2-3-5树:一种非常高效的多路查找树,它将3节点拆分为两个子节点,以减少查找和插入操作的平均路径长度。
总体来说,多路查找树是一类高效的数据结构,可以用于存储和查询大量数据。其中,B树和其变体常常被用于数据库和文件系统等存储设备上。而2-3-5树则被认为是一种非常高效的多路查找树。