目录
B 树和 B + 树:数据结构的深度解析
一、引言
二、B 树和 B + 树的基本概念
(一)从二叉树到 B 树
(二)B + 树与 B 树的区别
三、B 树和 B + 树的应用场景
四、为何使用 B 树或 B + 树作为索引结构
五、B 树和 B + 树的特点总结
(一)B 树特点
(二)B + 树特点
六、学习建议
二叉树节点定义示例
二叉查找树插入节点示例方法
平衡二叉树(AVL 树)节点定义及左旋右旋示例方法(简化示意,实际更复杂)
B 树节点定义示例(简化示意,未包含完整 B 树操作逻辑)
B + 树节点定义示例(简化示意,未包含完整 B + 树操作逻辑)
在数据结构与算法领域,B 树和 B + 树是常常被提及的重要概念。不少小伙伴对它们存在疑惑,甚至质疑其在实际工作中的用处,以及为何面试中经常出现相关问题。今天,我们就来深入剖析 B 树和 B + 树,解答这些疑问,并分享一些相关的学习资源。
以下是根据上述视频内容涉及的部分概念,编写的一些相关 Java 示例代码,用于辅助理解其中提到的数据结构相关内容:
class BinaryTreeNode {
int value;
BinaryTreeNode left;
BinaryTreeNode right;
public BinaryTreeNode(int value) {
this.value = value;
this.left = null;
this.right = null;
}
}
class BinarySearchTree {
private BinaryTreeNode root;
public void insert(int value) {
root = insertRecursive(root, value);
}
private BinaryTreeNode insertRecursive(BinaryTreeNode current, int value) {
if (current == null) {
return new BinaryTreeNode(value);
}
if (value < current.value) {
current.left = insertRecursive(current.left, value);
} else if (value > current.value) {
current.right = insertRecursive(current.right, value);
}
return current;
}
}
class AVLTreeNode {
int value;
AVLTreeNode left;
AVLTreeNode right;
int height;
public AVLTreeNode(int value) {
this.value = value;
this.left = null;
this.right = null;
this.height = 1;
}
}
class AVLTree {
private AVLTreeNode root;
// 获取节点高度
private int height(AVLTreeNode node) {
if (node == null) {
return 0;
}
return node.height;
}
// 获取平衡因子
private int getBalanceFactor(AVLTreeNode node) {
if (node == null) {
return 0;
}
return height(node.left) - height(node.right);
}
// 右旋操作(简化示意)
private AVLTreeNode rightRotate(AVLTreeNode y) {
AVLTreeNode x = y.left;
AVLTreeNode T2 = x.right;
x.right = y;
y.left = T2;
y.height = Math.max(height(y.left), height(y.right)) + 1;
x.height = Math.max(height(x.left), height(x.right)) + 1;
return x;
}
// 左旋操作(简化示意)
private AVLTreeNode leftRotate(AVLTreeNode x) {
AVLTreeNode y = x.right;
AVLTreeNode T2 = y.left;
y.left = x;
x.right = T2;
x.height = Math.max(height(x.left), height(x.right)) + 1;
y.height = Math.max(height(y.left), height(y.right)) + 1;
return y;
}
// 插入节点并保持平衡(简化示意,实际需更多处理)
public void insert(int value) {
root = insertRecursive(root, value);
}
private AVLTreeNode insertRecursive(AVLTreeNode current, int value) {
if (current == null) {
return new AVLTreeNode(value);
}
if (value < current.value) {
current.left = insertRecursive(current.left, value);
} else if (value > current.value) {
current.right = insertRecursive(current.right, value);
} else {
// 不允许插入重复值,这里可根据实际需求处理
return current;
}
current.height = 1 + Math.max(height(current.left), height(current.right));
int balanceFactor = getBalanceFactor(current);
// 左左情况
if (balanceFactor > 1 && value < current.left.value) {
return rightRotate(current);
}
// 右右情况
if (balanceFactor < -1 && value > current.right.value) {
return leftRotate(current);
}
// 左右情况
if (balanceFactor > 1 && value > current.left.value) {
current.left = leftRotate(current.left);
return rightRotate(current);
}
// 右左情况
if (balanceFactor < -1 && value < current.right.value) {
current.right = rightRotate(current.right);
return leftRotate(current);
}
return current;
}
}
import java.util.ArrayList;
import java.util.List;
class BTreeNode {
// 存储关键字
List keys = new ArrayList<>();
// 存储子节点
List children = new ArrayList<>();
boolean isLeaf;
public BTreeNode(boolean isLeaf) {
this.isLeaf = isLeaf;
}
// 添加关键字
public void addKey(int key) {
keys.add(key);
}
// 添加子节点
public void addChild(BTreeNode child) {
children.add(child);
}
}
import java.util.ArrayList;
import java.util.List;
class BPlusTreeNode {
// 存储关键字
List keys = new ArrayList<>();
// 存储指向子节点的指针(非叶子节点)或数据(叶子节点)
List
请注意,上述代码只是对视频中提及的数据结构概念进行简单的 Java 代码实现示例,用于辅助理解相关结构特点及操作逻辑的大致思路。真正完整且高效的实现这些数据结构会涉及到更多复杂的细节处理,比如节点的分裂、合并,数据的插入、删除以及平衡维护等操作,在实际应用场景中还需要根据具体需求进一步优化和完善。
B + 树是在 B 树基础上的增强。二者最大的区别有两点:
# 定义树节点类
class TreeNode:
def __init__(self, data=None):
self.data = data
self.children = []
# 创建一个简单的树(类似B树的节点存储数据方式)
root = TreeNode(10)
node2 = TreeNode(5)
node3 = TreeNode(15)
root.children = [node2, node3]
B 树和 B + 树一般应用在文件系统或者数据库系统中,主要目的是减少磁盘 I/O 带来的性能损耗。以 MySQL 的 InnoDB 为例,当执行select
语句查询一条数据时,InnoDB 需要从磁盘读取数据,这个过程涉及磁盘 I/O 和磁盘随机 I/O。磁盘 I/O 的性能很低,特别是随机磁盘 I/O,因为磁盘 I/O 需要经过系统传逻辑地址、磁盘控制电路翻译物理地址、磁头寻道和旋转等步骤,耗时较长。所以 InnoDB 对存储在磁盘块上的数据建立索引,将索引数据以及索引列对应的磁盘地址以 B + 树的方式存储。这样在查找目标数据时,根据索引从 B + 树中查找,由于 B + 树的分支比较多,只需较少次数的磁盘 I/O 就能找到对应的数据。
原因是 AVL 树(平衡二叉树)的高度比 B + 树的高度要高,而树的高度意味着磁盘 I/O 的数量。为了减少磁盘 I/O 的次数,文件系统或者数据库才会采用 B 树或者 B + 树。
刷面试题虽能在短期内提升应试能力,但可能存在准备的问题没考,没准备的问题却被问到的情况。所以我们要深度梳理技术体系,再结合面试文档整体梳理,确保能通过面试。对于 Java 技术的系统化梳理,可以选择酷跑科技的架构涨薪班课程提升解决问题的能力。如果对课程内容有疑惑,可以在评论区留言咨询。希望大家通过对 B 树和 B + 树的学习,能更好地理解数据结构在实际应用中的重要性,提升自己的知识储备。