1、定义树的节点
java
package com.alibaba.cloud.utils;
import java.util.ArrayList;
import java.util.List;
public class BPlusTreeNode {
private boolean isLeaf; // 是否为叶子节点
private List<Integer> keys; // 存储键值
private List<BPlusTreeNode> children; // 存储子节点引用
private BPlusTreeNode next; // 叶子节点的下一个节点引用
public BPlusTreeNode(boolean isLeaf) {
this.isLeaf = isLeaf;
this.keys = new ArrayList<>();
if (!isLeaf) {
this.children = new ArrayList<>();
}
this.next = null;
}
public boolean isLeaf() {
return isLeaf;
}
public List<Integer> getKeys() {
return keys;
}
public List<BPlusTreeNode> getChildren() {
return children;
}
public BPlusTreeNode getNext() {
return next;
}
public void setNext(BPlusTreeNode next) {
this.next = next;
}
public void addKey(int key) {
keys.add(key);
}
public void addChild(BPlusTreeNode child) {
if (!isLeaf) {
children.add(child);
}
}
public void removeKey(int index) {
keys.remove(index);
}
public void removeChild(int index) {
if (!isLeaf) {
children.remove(index);
}
}
public void insertKey(int index, int key) {
keys.add(index, key);
}
public void insertChild(int index, BPlusTreeNode child) {
if (!isLeaf) {
children.add(index, child);
}
}
public int getKeyCount() {
return keys.size();
}
public int getChildCount() {
return isLeaf ? 0 : children.size();
}
public int getKey(int index) {
return keys.get(index);
}
public BPlusTreeNode getChild(int index) {
return isLeaf ? null : children.get(index);
}
public boolean isFull(int order) {
return keys.size() >= order - 1;
}
public boolean isUnderflow(int order) {
int minKeys = isLeaf ? order / 2 : (order - 1) / 2;
return keys.size() < minKeys;
}
}
2、定义B+树
java
package com.alibaba.cloud.utils;
import java.util.ArrayList;
import java.util.List;
public class BPlusTree {
private BPlusTreeNode root;
private int order; // B+树的阶数
public BPlusTree(int order) {
this.order = order;
this.root = new BPlusTreeNode(true); // 初始根节点为叶子节点
}
// 插入操作
public void insert(int key) {
BPlusTreeNode root = this.root;
// 如果根节点已满,需要分裂
if (root.isFull(order)) {
BPlusTreeNode newRoot = new BPlusTreeNode(false);
newRoot.addChild(root);
splitChild(newRoot, 0);
this.root = newRoot;
insertNonFull(this.root, key);
} else {
insertNonFull(root, key);
}
}
// 向非满节点插入
private void insertNonFull(BPlusTreeNode node, int key) {
int i = node.getKeyCount() - 1;
if (node.isLeaf()) {
// 叶子节点直接插入键值
while (i >= 0 && key < node.getKey(i)) {
i--;
}
node.insertKey(i + 1, key);
} else {
// 非叶子节点,找到合适的子节点
while (i >= 0 && key < node.getKey(i)) {
i--;
}
i++;
// 如果子节点已满,分裂
if (node.getChild(i).isFull(order)) {
splitChild(node, i);
if (key > node.getKey(i)) {
i++;
}
}
insertNonFull(node.getChild(i), key);
}
}
// 分裂子节点
private void splitChild(BPlusTreeNode parent, int index) {
BPlusTreeNode child = parent.getChild(index);
BPlusTreeNode newChild = new BPlusTreeNode(child.isLeaf());
int mid = order / 2;
// 移动后半部分键值到新节点
for (int i = mid; i < child.getKeyCount(); i++) {
newChild.addKey(child.getKey(i));
}
child.getKeys().subList(mid, child.getKeyCount()).clear();
// 如果不是叶子节点,移动子节点
if (!child.isLeaf()) {
for (int i = mid; i < child.getChildCount(); i++) {
newChild.addChild(child.getChild(i));
}
child.getChildren().subList(mid, child.getChildCount()).clear();
}
// 处理叶子节点的next指针
if (child.isLeaf()) {
newChild.setNext(child.getNext());
child.setNext(newChild);
}
// 将中间键值提升到父节点
parent.insertKey(index, child.getKey(mid - 1));
parent.insertChild(index + 1, newChild);
}
// 查找操作
public boolean search(int key) {
return search(root, key);
}
private boolean search(BPlusTreeNode node, int key) {
int i = 0;
while (i < node.getKeyCount() && key > node.getKey(i)) {
i++;
}
if (i < node.getKeyCount() && key == node.getKey(i)) {
return true;
}
if (node.isLeaf()) {
return false;
}
return search(node.getChild(i), key);
}
// 范围查询
public List<Integer> rangeSearch(int min, int max) {
List<Integer> result = new ArrayList<>();
BPlusTreeNode leaf = findFirstLeaf(root, min);
while (leaf != null) {
for (int key : leaf.getKeys()) {
if (key >= min && key <= max) {
result.add(key);
} else if (key > max) {
return result;
}
}
leaf = leaf.getNext();
}
return result;
}
// 找到第一个可能包含min的叶子节点
private BPlusTreeNode findFirstLeaf(BPlusTreeNode node, int min) {
if (node.isLeaf()) {
return node;
}
int i = 0;
while (i < node.getKeyCount() && min > node.getKey(i)) {
i++;
}
return findFirstLeaf(node.getChild(i), min);
}
// 前序遍历
public void preOrder() {
preOrder(root);
System.out.println();
}
private void preOrder(BPlusTreeNode node) {
if (node != null) {
System.out.print(node.getKeys() + " ");
if (!node.isLeaf()) {
for (BPlusTreeNode child : node.getChildren()) {
preOrder(child);
}
}
}
}
// 遍历叶子节点
public void traverseLeafNodes() {
BPlusTreeNode leaf = findFirstLeaf(root, Integer.MIN_VALUE);
while (leaf != null) {
System.out.print(leaf.getKeys() + " ");
leaf = leaf.getNext();
}
System.out.println();
}
// 获取根节点
public BPlusTreeNode getRoot() {
return root;
}
// 获取阶数
public int getOrder() {
return order;
}
}
3、测试B+树
java
package com.alibaba.cloud.utils;
import java.util.List;
public class BPlusTreeTest {
public static void main(String[] args) {
// 创建一个阶数为3的B+树
BPlusTree bPlusTree = new BPlusTree(3);
// 插入测试数据
int[] keys = {10, 20, 5, 6, 12, 30, 7, 17};
for (int key : keys) {
bPlusTree.insert(key);
System.out.println("插入 " + key + " 后,B+树结构:");
bPlusTree.preOrder();
}
// 遍历叶子节点
System.out.println("\n遍历叶子节点:");
bPlusTree.traverseLeafNodes();
// 查找测试
int[] searchKeys = {6, 15, 20};
for (int key : searchKeys) {
boolean found = bPlusTree.search(key);
System.out.println("查找 " + key + ":" + (found ? "找到" : "未找到"));
}
// 范围查询测试
System.out.println("\n范围查询 5-20:");
List<Integer> rangeResult = bPlusTree.rangeSearch(5, 20);
System.out.println(rangeResult);
// 测试插入更多数据,触发分裂
System.out.println("\n插入更多数据:");
int[] moreKeys = {8, 9, 11, 13, 14, 15, 16, 18, 19, 21, 22};
for (int key : moreKeys) {
bPlusTree.insert(key);
}
System.out.println("插入后B+树结构:");
bPlusTree.preOrder();
System.out.println("遍历叶子节点:");
bPlusTree.traverseLeafNodes();
}
}
测试结果:
10 后,B+树结构:
10
插入 20 后,B+树结构:
10, 20
插入 5 后,B+树结构:
10\] \[5, 10\] \[20
插入 6 后,B+树结构:
5, 10\] \[5\] \[6, 10\] \[20
插入 12 后,B+树结构:
5\] \[5\] \[5\] \[10\] \[6, 10\] \[12, 20
插入 30 后,B+树结构:
5\] \[5\] \[5\] \[10, 12\] \[6, 10\] \[12\] \[20, 30
插入 7 后,B+树结构:
5, 10\] \[5\] \[5\] \[6, 10\] \[6\] \[7, 10\] \[12\] \[12\] \[20, 30
插入 17 后,B+树结构:
5\] \[5\] \[5\] \[5\] \[10\] \[6, 10\] \[6\] \[7, 10\] \[12, 20\] \[12\] \[17, 20\] \[30
遍历叶子节点:
5\] \[6\] \[7, 10\] \[12\] \[17, 20\] \[30
查找 6:找到
查找 15:未找到
查找 20:找到
范围查询 5-20:
5, 6, 7, 10, 12, 17, 20
插入更多数据:
插入后B+树结构:
5, 6\] \[5\] \[5\] \[5\] \[5\] \[5\] \[5\] \[5\] \[6\] \[6\] \[6\] \[6\] \[6\] \[6\] \[6\] \[7, 10\] \[7\] \[7\] \[7\] \[7\] \[7\] \[7\] \[10\] \[10\] \[10\] \[10\] \[8, 10\] \[8\] \[9, 10\] \[12\] \[12\] \[12\] \[12\] \[12\] \[11, 12\] \[13\] \[13\] \[13\] \[13\] \[13\] \[14, 17\] \[14\] \[14\] \[14\] \[17\] \[15, 17\] \[15\] \[16, 17\] \[18\] \[18\] \[18\] \[20, 21\] \[19, 20\] \[21\] \[22, 30
遍历叶子节点:
5\] \[6\] \[7\] \[8\] \[9, 10\] \[11, 12\] \[13\] \[14\] \[15\] \[16, 17\] \[18\] \[19, 20\] \[21\] \[22, 30
进程已结束,退出代码为 0
-
BPlusTreeNode.java - 定义B+树节点结构
- 包含键值列表、子节点列表(非叶子节点)、叶子节点的next指针
- 提供节点的基本操作方法
-
BPlusTree.java - 实现B+树核心功能
- 插入操作:支持节点分裂
- 查找操作:快速定位键值
- 范围查询:利用叶子节点的链表结构
- 遍历操作:前序遍历和叶子节点遍历
-
BPlusTreeTest.java - 测试B+树功能
- 插入测试数据
- 验证查找和范围查询
- 测试节点分裂情况
B+树特点
- 所有键值都存储在叶子节点中,非叶子节点仅用于索引
- 叶子节点形成有序链表,支持范围查询
- 插入时自动分裂节点,保持树的平衡
- 查找路径长度一致,查询性能稳定
