Java B+ 树 简单实现

本文暂时实现了B+树插入,查找和范围查找功能,由于需求原因,暂未实现删除功能。
本文B+树Key为int类型,Value支持泛型。
Github开源地址:https://github.com/1161140118/HIT-DataBase-Lab4/tree/master/src
感激您的star


数据结构:

主类BPlusTree,维护root结点。
非叶结点Node,维护Key和子节点索引。
叶结点Leaf,维护Key和Value索引。

主体思路:

主类BPlusTree维护root结点。
叶结点记录父节点,非根中间结点记录父节点。

初始状态下,root结点为Node,仅记录1个索引唯一叶子。
插入开始,由root->leaf,插入到leaf;
随着插入进行,leaf发生分裂,中间结点Key拷贝上溢到父节点Node类型结点。
倒数第二层的Node结点的分裂由Leaf分裂上溢引发,

Node结点的分裂分为两种情况处理:

1. Node结点为根节点
下推:
当前根节点分裂为两个新子节点left和right,分裂依据的Key称为splitKey。
初始化当前结点,即重置当前结点的Key List只包含splitKey,而子节点引用只包含left和right。
即,当前节点仍未根节点,但Key和子节点引用都发生了改变。
2. Node结点为非根结点
上溢:
当前节点分裂出一个新节点,在父节点中插入splitKey和新节点的索引。

主类BPlusTree数据结构:

public class BPlusTree {
    public static int rank;
    private Node root;
    
    public BPlusTree(int rank) {
        BPlusTree.rank = rank;
        // 根节点始终维护同一个Node
        this.root = new Node(null, false);
    }
    
    public void insertData(int key,V ref) {
        root.insertData(key, ref);
    }
    
    
    /**
     * 递归调用Node.search方法,查询索引
     * @param key 索引键
     * @return
     */
    public V search(int key) {
        return root.search(key);
    }
    
    /**
     * range search from start to end
     * @param start start key, include 
     * @param end   end key, include
     * @return  value list
     */
    public List rangeSearch(int start,int end){
        return root.rangeSearch(start, end);
    }

插入部分算法
非叶结点Node:上溢,或下推

    /**
     * 子节点分裂时,由子节点调用
     * @param key 待插入关键字
     */
    protected void insertNode(int key, Node node) {
        System.out.println("非叶节点插入 " + key);
        int i = 0;
        for (i = 0; i < keyList.size(); i++) {
            if (key < keyList.get(i)) {
                break;
            }
        }
        keyList.add(i, key);
        childNodes.add(i + 1, node);
        /**
         * 判定分裂
         * 非根节点:上溢分裂
         * 根节点:下推分裂,当前节点分裂为两个新节点,当前节点重置,新节点作为当前节点子节点
         */
        if (keyList.size() >= BPlusTree.rank) {
            System.out.println("非叶节点分裂." + key);
            int split = BPlusTree.rank / 2;
            Integer splitKey = keyList.get(split);
            // 新节点:左右子树
            List leftKeyList = new LinkedList<>();
            List> leftChilds = new LinkedList<>();
            List rightKeyList = new LinkedList<>();
            List> rightChilds = new LinkedList<>();
            // copy
            for (int j = 0; j < split; j++) {
                leftKeyList.add(keyList.get(j));
                leftChilds.add(childNodes.get(j));
            }
            leftChilds.add(childNodes.get(split));
            for (int j = split + 1; j < keyList.size(); j++) {
                rightKeyList.add(keyList.get(j));
                rightChilds.add(childNodes.get(j));
            }
            rightChilds.add(childNodes.get(keyList.size()));


            this.keyList.clear();
            this.childNodes.clear();
            Node leftChild = new Node(this, leftKeyList, leftChilds);
            Node rightChild = new Node(this, rightKeyList, rightChilds);

            if (parent != null) {
                // 上溢
                this.keyList = leftKeyList;
                this.childNodes = leftChilds;
                parent.insertNode(splitKey, rightChild);
                System.out.println("非叶节点上溢 "+splitKey);
            } else {
                // 根节点下推
                // 更新当前结点,分裂后下推
                this.keyList.add(splitKey);
                this.childNodes.add(leftChild);
                this.childNodes.add(rightChild);
                System.out.println("根节点下推"+splitKey);
            }
        }
    }

叶结点Leaf:上溢

    /**
     * 在叶结点插入索引,
     * 可能触发分裂
     * @return 
     */
    @Override
    protected void insertData(int key, V ref) {
        int i = 0;
        for (i = 0; i < refList.size(); i++) {
            if (key < keyList.get(i)) {
                break;
            }
        }
        keyList.add(i, key);
        refList.add(i, ref);
        System.out.println("叶结点插入"+key);
        
        /**
         * 判定分裂
         * 例:4阶树,达到4个数据结点,分裂,上溢第三个key(index=2) 
         */
        if (refList.size() >= BPlusTree.rank) {
            int split = BPlusTree.rank / 2;
            List newKeyList = new LinkedList<>();
            List newRefList = new LinkedList<>();
            // copy
            for (int j = split; j < keyList.size(); j++) {
                newKeyList.add(keyList.get(j));
                newRefList.add(refList.get(j));
            }
            // remove
            int len  = keyList.size();
            for (int j = split; j < len; j++) {
                // 执行 size-split 此
                keyList.remove(split);
                refList.remove(split);
            }
            Leaf newLeaf = new Leaf(parent, newKeyList, newRefList);
            this.next = newLeaf;
            System.out.println("叶结点分裂."+key);
            parent.insertNode(newKeyList.get(0), newLeaf);
        }
        System.out.println(key+" 插入最终结果 "+keyList);
        // 检查不变量
        if (keyList.size() != refList.size()) {
            System.err.println("Error : The length of keylist not equals to reflist!");
        }
    }

你可能感兴趣的:(Java B+ 树 简单实现)