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+树特点

  • 所有键值都存储在叶子节点中,非叶子节点仅用于索引
  • 叶子节点形成有序链表,支持范围查询
  • 插入时自动分裂节点,保持树的平衡
  • 查找路径长度一致,查询性能稳定
相关推荐
她说彩礼65万18 小时前
C# 实现简单的日志打印
开发语言·javascript·c#
绿浪198418 小时前
c# 中结构体 的定义字符串字段(性能优化)
开发语言·c#
HoneyMoose18 小时前
Jenkins Cloudflare 部署提示错误
java·servlet·jenkins
阿丰资源18 小时前
基于SpringBoot的物流信息管理系统设计与实现(附资料)
java·spring boot·后端
Predestination王瀞潞18 小时前
Java EE3-我独自整合(第四章:Spring bean标签的常见配置)
java·spring·java-ee
overmind18 小时前
oeasy Python 121[专业选修]列表_多维列表运算_列表相加_列表相乘
java·windows·python
资深数据库专家18 小时前
总账EBS 应用服务器1 的监控分析
java·网络·数据库
房开民18 小时前
可变参数模板
java·开发语言·算法
t***54419 小时前
如何在现代C++中更有效地应用这些模式
java·开发语言·c++
_深海凉_19 小时前
LeetCode热题100-最小栈
java·数据结构·leetcode