Java实现B+树,体会B+树做索引的精妙

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

  1. BPlusTreeNode.java - 定义B+树节点结构

    • 包含键值列表、子节点列表(非叶子节点)、叶子节点的next指针
    • 提供节点的基本操作方法
  2. BPlusTree.java - 实现B+树核心功能

    • 插入操作:支持节点分裂
    • 查找操作:快速定位键值
    • 范围查询:利用叶子节点的链表结构
    • 遍历操作:前序遍历和叶子节点遍历
  3. BPlusTreeTest.java - 测试B+树功能

    • 插入测试数据
    • 验证查找和范围查询
    • 测试节点分裂情况

B+树特点

  • 所有键值都存储在叶子节点中,非叶子节点仅用于索引
  • 叶子节点形成有序链表,支持范围查询
  • 插入时自动分裂节点,保持树的平衡
  • 查找路径长度一致,查询性能稳定
相关推荐
楼田莉子2 小时前
C++数据结构:基数树
开发语言·数据结构·c++·学习
m0_518019482 小时前
C++中的命令模式实战
开发语言·c++·算法
ProgramHan2 小时前
十大排行榜——后端语言及要介绍
java·c++·python·php
小江的记录本2 小时前
【反射】Java反射 全方位知识体系(附 应用场景 + 《八股文常考面试题》)
java·开发语言·前端·后端·python·spring·面试
callJJ2 小时前
Ant Design Table 批量操作踩坑总结 —— 从三个 Bug 看前端表格开发的共性问题
java·前端·经验分享·bug·管理系统
没有bug.的程序员2 小时前
100%采样率引发的全线熔断:Spring Boot 链路追踪的性能绞杀与物理级调优
java·spring boot·后端·生产·熔断·调优·链路追踪
不懒不懒2 小时前
【基于 CNN 的食物图片分类:数据增强、最优模型保存与学习率调整实战】
开发语言·python
木井巳2 小时前
【多线程】常见的锁策略及 synchronized 的原理
java·开发语言
代码改善世界2 小时前
【C++初阶】类和对象(二):默认成员函数详解与日期类完整实现
开发语言·c++