B+树详解(附代码)

1.B+树的背景

我们谈起B+树,可能最先想到它是MySQL中底层存储所采用的数据结构,其实B+树和二叉树、平衡二叉树一样,都是经典的数据结构。B+树由B树和索引顺序访问方法(ISAM,是不是很熟悉?对,这也是MyISAM引擎最初参考的数据结构)演化而来,MongDB中就采用的B树。
为啥MySQL中不采用B树,而采用B+树呢?(或者说MongDB中为啥不采用B+树呢?)
这是因为MySql是关系型数据库,数据的关联性是非常强的,区间访问是常见的一种情况,底层索引组织数据使用B+树,B+树由于数据全部存储在叶子节点,并且通过指针串在一起,这样就很容易的进行区间遍历甚至全部遍历。MongoDB使用B树,所有节点都有Data域,只要找到指索引就可以进行访问,单次查询从结构上来看要快于MySql。
5阶B+树

2.B+树的定义

B+树是数据库所需而出现的一种B树的变形树。(这里借用王道书中,对B+树的定义)
一棵m阶的B+树需满足下列条件:
1)每个分支节点最多有m棵子树(子节点)。
2)非叶根节点至少有两棵子树,其他每个分支节点至少有⌈m/2⌉棵子树。
3)节点的子树与关键字个数相等。
4)所有叶节点包含全部关键字及指向相应记录的指针,而且叶节点中将关键字按大小顺序排列,并且相邻叶节点按大小顺序相互链接起来,m阶的B+树最多有m个关键字。
5)所有分支节点(可看成索引的索引)中仅包含他的各个子节点(即下一级的索引块)中关键字的最大值及指向其子节点的指针。

m阶的B+树与B树的主要差异在于:

  • 在B+树中,具有n个关键字的节点只含有n棵子树,即每个关键字对应一棵子树;而在B树中,具有n个关键字的节点含有(n+1)棵子树。(节点与子树个数的对应关系)
  • 在B+树中,每个节点(非根内部节点)关键字个数n的范围是[⌈m/2⌉, m](根节点:[1,m]),在B树中,每个节点(非根内部节点)关键字个数n的范围是[⌈m/2-1⌉,m-1](根节点:[1,m-1])。(每个节点关键字个数的范围)
  • 在B+树中,叶节点包含信息,所有非叶节点仅起到索引作用,非叶节点中的每个索引项只含有对应子树的最大关键字和指向该子树的指针,不含有该关键字对应记录的存储地址。
  • 在B+树中,叶节点包含了全部关键字,即在非叶节点中出现的关键字也会出现在叶节点中;而在B树中,叶节点包含的关键字和其他节点包含的关键字是不重复的。(叶节点包含全部关键字)

3.节点设计

isLeaf:起初想为叶子节点和非叶子节点设计两个数据结构,然后发现两个数据结构不如一个数据结构方便管理,所以选择一个结构,既能表示叶子节点,又能表示非叶子节点。(boolean)
parent:node的父节点,每次需要分裂节点,需要找到分裂节点的父节点,将新生成的节点作为父节点的子节点。(Node<K, V>)
nodeIndex:表示该节点node在父节点的索引值,这是因为当父节点的节点个数非常多的时候,直接根据索引找到node在父节点中的位置,如果没有nodeIndex,顺序查找未免太费时间了,这里属于是空间换时间了。(int)
degree:度,也是阶数,表示该节点能存储几个元素entry。(int)
entries:存储元素entry的列表;V是你传进来的类型,K是根据V生成的hash值,由KV共同构成Entry。(List<Entry<K, V>>)
nodeList:如果是叶子节点,则存储左节点和右节点;如果是非叶子节点,则存储子节点。(List<Node<K, V>>)
如下所示:
model

  • 叶子节点
    在这里插入图片描述

  • 非叶子节点
    在这里插入图片描述

4.B+树的初始化

degree:初始化B+树时,需要完成node的degree的赋值操作,表示degree阶B+树。
head:头节点,指向B+树的最高层的节点,增删查都需要用到head,head指向的节点的nodeIndex为-1。
half:等于(degree + 1) / 2;很多时候都会用到这个,例如节点分裂,节点合并时的可借和不可借,详情见后面代码。
初始化之后会生成一个叶节点,head指向叶节点
B+树的初始化

5.插入操作

当该节点的 entries的元素个数 <= degree 则能直接插入,插入之后,有两种情况:

  • entries的元素个数 <= degree ,直接返回
    直接插入后返回

  • entries的元素个数 > degree ,分裂

分裂(文字流程在代码注释里,别怕,写得很详细了)
1)node不为head节点时,生成newNode,层数不变
(1)叶子节点分裂

插入99
分裂叶节点

(2)非叶子节点分裂

插入99后的非叶节点
非叶节点分裂
分裂后的父节点对应entry的修改

2)node为head节点时,生成newHead和newNode,升层
(1)head为叶子节点

插入K6
生成newHead和newNode
newNode完成赋值
newHead完成赋值
node完成赋值

(2)head为非叶子节点

插入到K17时
父节点变化
叶节点分裂
生成newHead和newNode,且nodeNode赋值
newHead的赋值
node的赋值

6.删除元素

当该节点的 entries的元素个数 > half 则能直接删除,删除之后,有两种情况:

  • entries的元素个数 >= half,直接返回
    删除K4

  • entries的元素个数 < half,node借元素(左借,右借)和合并(左合并,右右合并),合并完是否降层

借(同一个父节点下,优先左借)
1)叶子节点借
(1)左借

删除K7
删除K7之后,还未借
借完后

(2)右借

删除K3
删除完,还未借之前
借完后

2)非叶子节点借
(1)左借

删除130
删除130后,叶子节点未合并前
叶子节点合并后
非叶子节点左借后

(2)右借

删除130
删除130后,叶子节点未合并前
非叶子节点未右借前
非叶子节点右借完后

合并(同一个父节点下,优先左合并,父节点的entries.size和nodeList.size()都要-1,层数是否降低)
1)叶子节点的合并
(1)叶子节点的左合并

删除94
删除84后,未合并前
entry移动
node赋值
左右节点重新指向

(2)叶子节点的右合并

删除30
删除30后,未合并前
父节点对应entry修改
node和右节点的赋值

2)非叶子节点的合并
(1)非叶子节点的左合并

删除160
删除160未合并前
node完成赋值
叶节点合并
父节点赋值过程
父节点赋值完成

(2)非叶子节点的右合并

删除10
删除之后,合并前
叶子合并
父节点合并
父节点的父节点对应删除

降层
1)合并之后无子节点

删除40
删除后,合并前
entry移动
父节点

2)合并之后有子节点

删除120
叶子节点删除120后,未合并前
node的赋值过程
父节点的赋值
右节点的赋值
父节点的合并
父节点合并完成
head节点清空赋值
head重新指向

7.代码

1)Entry
package com.thinking.tree;

public class Entry<K, V> {

    private K key;
    private V value;
    public void setKey(K key) {
        this.key = key;
    }
    public K getKey() {
        return this.key;
    }
    public void setValue(V value) {
        this.value = value;
    }
    public V getValue() {
        return this.value;
    }
    public Entry(K key, V value) {
        this.key = key;
        this.value = value;
    }
    public Entry() {
    }
    @Override
    public String toString() {
        return "key: " + this.key;
    }
}

2)Node
package com.thinking.tree;

import java.util.Comparator;
import java.util.List;

public class Node<K, V> {

    private boolean isLeaf = true;

    private Node<K, V> parent;

    //该节点在其父节点中处于的索引处
    private int nodeIndex;

    private int degree;

    private List<Entry<K, V>> entries;

    private List<Node<K, V>> nodeList;

    //键值比较函数对象,如果采用倒序或者其它排序方式,传入该对象
    private Comparator<K> kComparator;

    //比较两个key,如果没有传入自定义排序方式则采用默认的升序
    public int compare(K key1, K key2) {
        return this.kComparator == null ? ((Comparable<K>) key1).compareTo(key2) : kComparator.compare(key1, key2);
    }

    /**
     * 在node中二分查找key值,无论存不存在,都要返回一个index
     * @param key
     * @return
     * key存在,返回对应的index
     * key不存在,返回大于key的最小值的索引
     *   若没有大于key的值,则返回最大索引值
     *   如下数组:
     *   ---------------------------------------------
     *   | 0 | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 |
     *   ---------------------------------------------
     *   查找-1:返回index=0
     *   查找 0:返回index=0
     *   查找 1:返回index=1
     *   查找40:返回index=4
     *   查找71:返回index=8
     *   查找80:返回index=8
     *   查找81:返回index=8
     *
     *
     *   查找-1:返回index=0
     *   查找1 :返回index=1
     *   查找11:返回index=2
     *   查找21:返回index=3
     *   查找31:返回index=4
     *   查找41:返回index=5
     *   查找51:返回index=6
     *   查找61:返回index=7
     *   查找71:返回index=8
     *   查找81:返回index=8
     */
    public SearchResult search(K key) {
        int left = 0; //左指针
        int right = this.getEntries().size() - 1;//右指针
        int mid = (left + right) / 2;//中间的位置
        boolean isExist = false; //判断是否找到了
        int searchIndex = 0; //要找的关键字的下标
        int maxIndex = right;//最大索引值
        while(left <= right){
            mid = (left + right)/2;//中间的位置
            Entry<K,V> midEntry = this.entries.get(mid);//拿到中间的关键字
            int comparator = this.compare(key, midEntry.getKey());
            if(comparator == 0){//找到了
                isExist = true;
                searchIndex = mid;
                break;
            }else if(comparator==1){//在右侧
                left = mid + 1;
            }else{//midKey大于key 在左侧
                right = mid - 1;
            }
        }
        //二分查找结束了
        //B树首次插入节点,会出现left = 0且right = -1的情况
        if(isExist == false) {
            if (right == -1) {
                searchIndex = left;
            } else {
                //能走到这里说明left > right
                //选择较大的哪个值,但是left可能会出现大于最大索引值的情况,这时取最大值
                if (left <= maxIndex) {
                    searchIndex = left;
                } else {
                    searchIndex = maxIndex;
                }
            }
        }
        List<Entry<K, V>> entries = this.getEntries();

        /**要插入的key并不存在,但是我们参考如下
         *  ---------------------------------------------
         *  | 0 | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 |
         *  ---------------------------------------------
         *  查找-1:返回index=0
         *  查找 0:返回index=0
         *  查找 1:返回index=1
         *  查找40:返回index=4
         *  查找71:返回index=8
         *  查找80:返回index=8
         *  查找81:返回index=8
         *
         *  我们插入71返回index=8,没问题
         *  我们插入81也返回index=8,就有问题了,应返回index=9
         *  所以这里需要判别一下
         *      如果maxIndex == -1,说明叶子节点中还没有元素,则insertIndex为0
         *      如果maxIndex >= 0
         *          则需要判别key和maxKey的关系
         *          如果key <= maxKey, insertIndex = searchIndex
         *          如果key > maxKey, insertIndex = searchIndex + 1
         */
        int insertIndex = -1;
        if (maxIndex == -1) {
            insertIndex = 0;
        } else {
            if (this.compare(key, entries.get(maxIndex).getKey()) == 1) {
                insertIndex = searchIndex + 1;
            } else {
                insertIndex = searchIndex;
            }
        }
        return new SearchResult(
                isExist,
                searchIndex,
                insertIndex
        );
    }


    public Node(int degree) {
        this.degree = degree;
    }

    public boolean isLeaf() {
        return isLeaf;
    }

    public void setLeaf(boolean leaf) {
        isLeaf = leaf;
    }

    public List<Entry<K, V>> getEntries() {
        return entries;
    }

    public void setEntries(List<Entry<K, V>> entries) {
        this.entries = entries;
    }

    public List<Node<K, V>> getNodeList() {
        return nodeList;
    }

    public void setNodeList(List<Node<K, V>> nodeList) {
        this.nodeList = nodeList;
    }

    public Node<K, V> getParent() {
        return parent;
    }

    public void setParent(Node<K, V> parent) {
        this.parent = parent;
    }

    public int getNodeIndex() {
        return nodeIndex;
    }

    public void setNodeIndex(int nodeIndex) {
        this.nodeIndex = nodeIndex;
    }

    public int getDegree() {
        return degree;
    }

    public Node<K, V> getLeftNode() {
        return isLeaf() == true ? nodeList.get(0) : null;
    }

    public Node<K, V> getRightNode() {
        return isLeaf() == true ? nodeList.get(1) : null;
    }

    public void setLeftNode(Node<K, V> leftNode) {
        if (isLeaf()) {
            this.getNodeList().set(0, leftNode);
        }
    }

    public void setRightNode(Node<K, V> rightNode) {
        if (isLeaf()) {
            this.getNodeList().set(1, rightNode);
        }
    }

}

3)SearchResult
package com.thinking.tree;

public class SearchResult<K, V> {
    private boolean exist;
    private int searchIndex;
    private int insertIndex;


    public SearchResult(boolean exist, int searchIndex, int insertIndex) {
        this.exist = exist;
        this.searchIndex = searchIndex;
        this.insertIndex = insertIndex;

    }

    public boolean isExist() {
        return exist;
    }

    public void setExist(boolean exist) {
        this.exist = exist;
    }

    public int getSearchIndex() {
        return searchIndex;
    }

    public void setSearchIndex(int searchIndex) {
        this.searchIndex = searchIndex;
    }

    public int getInsertIndex() {
        return insertIndex;
    }

    public void setInsertIndex(int insertIndex) {
        this.insertIndex = insertIndex;
    }
}

4)BPlusTree
package com.thinking.tree;


import javafx.util.Pair;

import java.util.*;


public class BPlusTree<V> {

    //默认是5阶树,度为5,当entries.size() = 6 时分裂
    private static int DEFAULT_DEGREE = 5;
    private int degree;
    private Node<Integer, V> head= null;
    private int half;

    public BPlusTree() {
        this(DEFAULT_DEGREE);
        this.degree = DEFAULT_DEGREE;
    }
    /**
     * 初始化时,完成head的指向,保证head随时都指向非叶子节点,减少head指向节点的判别
     * @param degree
     *  degree为奇数的时候,this.degree = degree
     *  degree为偶数的时候,this.degree = degree + 1
     *  实际使用的时候,degree会比较大,考虑奇偶意义不大,且会增加代码量,不易理解分裂和合并,所以这里只考虑一种情况,为偶数的情况
     */

    public BPlusTree(int degree) {
        head = newLeafNode();
        this.degree = degree % 2 == 0 ? degree + 1 : degree;
        this.half = (this.degree + 1)/2;
    }

    private Node<Integer, V> newLeafNode() {
        Node<Integer, V> node = new Node<>(this.degree);
        node.setLeaf(true);
        ArrayList<Entry<Integer, V>> entries = new ArrayList<>(this.degree + 1);
        node.setEntries(entries);
        ArrayList<Node<Integer, V>> nodeList = new ArrayList<>(2);
        nodeList.addAll(Arrays.asList(null, null));
        node.setNodeList(nodeList);
        node.setParent(null);
        node.setNodeIndex(-1);
        return node;
    }

    private Node<Integer, V> newBranchNode() {
        Node<Integer, V> node = new Node<>(this.degree);
        node.setLeaf(false);
        ArrayList<Entry<Integer, V>> entries = new ArrayList<>(this.degree + 1);
        ArrayList<Node<Integer, V>> nodeList = new ArrayList<>(this.degree + 1);
        node.setEntries(entries);
        node.setNodeList(nodeList);
        node.setParent(null);
        node.setNodeIndex(-1);
        return node;
    }

    private Entry<Integer, V> newEntry(Integer key, V val) {
        return new Entry<Integer, V>(key, val);
    }

    /**
     * 添加元素
     * @param val 插入元素
     */
    public void insert(V val) {
        //可根据val的hashCode生成key,但是为了测试方便且类比Mysql的主键
        // 在mysql中,如果随机插入,在mysql中会频繁分页,使得插入性能降低,当然我们这里并不会考虑这些,类比学习还是可以的
        // 故采用Integer类型的key
        int key = val.hashCode();
        insert(key, val);
    }

    public void delete(V val) {
        int key = val.hashCode();
        deleteKey(key);
    }

    /**
     *
     * @param key 根据key,找到插入位置
     * @param val 插入元素
     */
    public void insert(Integer key, V val) {
        Node<Integer, V> leafNode = searchNode(key);
        SearchResult search = leafNode.search(key);
        if (search.isExist()) {
            return;
        } else {
            //需要插入的key在树中不存在
            int insertIndex = search.getInsertIndex();
            List<Entry<Integer, V>> entries = leafNode.getEntries();
            Entry<Integer, V> newEntry = newEntry(key, val);
            entries.add(insertIndex, newEntry);
            loopChangeParent(leafNode);
            if (entries.size() <= this.degree) {
                return;
            } else {
                splitNode(leafNode);
            }
        }
    }

    /**
     * 从head开始查找key,找到key对应的叶子节点结束
     * 在没有插入的时候,没有叶子节点,自己造一个叶节点嘛
     * @param key
     * @return
     */
    private Node<Integer, V> searchNode(Integer key) {
        if (head.isLeaf()) {
            return head;
        }
        Node<Integer, V> node = head;
        //非叶子节点时循环,跳出循环时,说明找到了叶子节点
        while (!node.isLeaf()){
            SearchResult search = node.search(key);
            int index = search.getSearchIndex();
            node = node.getNodeList().get(index);
        }
        return node;
    }

    public boolean isExist(Integer key) {
        Node<Integer, V> leafNode = searchNode(key);
        SearchResult search = leafNode.search(key);
        return search.isExist();
    }

    /**
     * 如果node.entries的最后一个entry和node.parent.entries的最后一个entry不同,则需要变更
     * @param node
     */
    private void loopChangeParent(Node<Integer, V> node) {
        if (node.getParent() == null) {
            return;
        }
        List<Entry<Integer, V>> parentEntries = node.getParent().getEntries();
        List<Entry<Integer, V>> entries = node.getEntries();
//        if (parentEntries.size() == 0) {
//            parentEntries.add(entries.get(entries.size() - 1));
//            return;
//        }
        Entry<Integer, V> parentLastEntry = parentEntries.get(parentEntries.size() - 1);
        Entry<Integer, V> lastEntry = entries.get(entries.size() - 1);
        if (parentLastEntry == lastEntry) {
            return;
        }
        parentEntries.set(node.getNodeIndex(), lastEntry);
        loopChangeParent(node.getParent());
    }

    /**
     * 节点分裂
     * @param node 需要分裂的节点
     *             当node为head节点时 生成newHead和newNode
     *                  head为叶子节点
     *                      1)newNode.setParent(newHead)
     *                      2)half个entry需要移动到newHead
     *                      3)newNode.setLeftNode(node)
     *                      4)分别将node和newNode的最后一个Entry加入到newHead的entries,并且将node,newNode加入到newHead的nodeList中
     *                      4)node.setRightNode(newNode)
     *                  head为非叶子节点
     *                      1)newNode.setParent(newHead)
     *                      2)half个entry需要移动
     *                      3)half个node需要移动,且它们的父节点要指向newNode,它们的NodeIndex也需要变更
     *             当node不为head节点时 生成newNode
     *                  叶子节点分裂
     *                      1)newNode.setParent(node.getParent())
     *                      2)half个entry需要移动
     *                      3)如果原本存在右节点,则需完成newNode.setRightNode和rightNode.setLeftNode
     *                      4)newNode.setNodeIndex(node.getNodeIndex() + 1)
     *                  非叶子节点分裂
     *                      1)newNode.setParent(node.getParent())
     *                      2)half个entry需要移动
     *                      3)half个node需要移动,且它们的父节点要指向newNode,它们的NodeIndex也需要变更
     *
     */
    private void splitNode(Node<Integer, V> node) {
        if (node == head) {
            if (node.isLeaf()) {
                //分裂节点为head,会生成两个新节点
                Node<Integer, V> newNode = newLeafNode();
                Node<Integer, V> newHead = newBranchNode();
                // ===================newNode的赋值过程===================
                newNode.setParent(newHead);
                newNode.setNodeIndex(1);
                for (int i = 0 ; i < half; i++) {
                    newNode.getEntries().add(0,node.getEntries().get(this.degree - i));
                    node.getEntries().remove(this.degree - i);
                }
                newNode.setLeftNode(node);
                //===================newHead节点的赋值过程===================
                //newHead.setParent(null);
                //newHead.setNodeIndex(-1);
                newHead.getEntries().add(node.getEntries().get(node.getEntries().size() - 1));
                newHead.getEntries().add(newNode.getEntries().get(node.getEntries().size() - 1));
                newHead.getNodeList().add(node);
                newHead.getNodeList().add(newNode);
                // ===================head节点的赋值过程===================
                node.setParent(newHead);
                node.setNodeIndex(0);
                node.setRightNode(newNode);
                //===================head重新指向===================
                head = newHead;
            } else {
                //分裂节点为head,会生成两个新节点
                Node<Integer, V> newNode = newBranchNode();
                Node<Integer, V> newHead = newBranchNode();
                newNode.setParent(newHead);
                newNode.setNodeIndex(1);
                // ===================newNode的赋值过程===================
                newNode.setParent(newHead);
                newNode.setNodeIndex(1);
                for (int i = 0 ; i < half; i++) {
                    newNode.getEntries().add(0,node.getEntries().get(this.degree - i));
                    node.getEntries().remove(this.degree - i);
                    newNode.getNodeList().add(0, node.getNodeList().get(this.degree - i));
                    node.getNodeList().get(this.degree - i).setParent(newNode);
                    node.getNodeList().remove(this.degree - i);
                }
                changeRightNodeIndex(newNode.getNodeList().get(0), 0);
                newNode.setLeftNode(node);
                //===================newHead节点的赋值过程===================
                //newHead.setParent(null);
                //newHead.setNodeIndex(-1);
                newHead.getEntries().add(node.getEntries().get(node.getEntries().size() - 1));
                newHead.getEntries().add(newNode.getEntries().get(node.getEntries().size() - 1));
                newHead.getNodeList().add(node);
                newHead.getNodeList().add(newNode);
                // ===================head节点的赋值过程===================
                node.setParent(newHead);
                node.setNodeIndex(0);
                //===================head重新指向===================
                head = newHead;
            }
        } else {
            //分裂节点不为head,只会生成一个节点,这个节点可能时叶子节点,也可能时非叶子节点
            if (node.isLeaf()) {
                //node为叶子节点,则新生成一个叶子节点
                Node<Integer, V> newNode = newLeafNode();
                // ===================newNode的赋值过程===================
                newNode.setParent(node.getParent());
                newNode.setNodeIndex(node.getNodeIndex() + 1);

                for (int i = 0; i < half; i++) {
                    newNode.getEntries().add(0,node.getEntries().get(this.degree - i));
                    node.getEntries().remove(this.degree - i);
                }
                newNode.setLeftNode(node);//完成newNode的左指向
                Node<Integer, V> rightNode = node.getRightNode();
                if (rightNode != null) {//如果小于,说明node存在右节点
                    newNode.setRightNode(rightNode);//完成newNode的右指向
                    rightNode.setLeftNode(newNode);
                    //此外从node第一个右节点开始(包括第一个),所有的右节点的nodeIndex都需要修改
                    if (isRightNodeInCommonParent(node, rightNode)) {
                        //如果rightNode和node在同一个父节点下
                        changeRightNodeIndex(rightNode, node.getNodeIndex() + 2);
                    }
                }
                // ===================node.parent节点的赋值过程===================
                Node<Integer, V> parent = node.getParent();
                parent.getEntries().set(node.getNodeIndex(), node.getEntries().get(node.getEntries().size() - 1));
                parent.getEntries().add(node.getNodeIndex() + 1, newNode.getEntries().get(newNode.getEntries().size() - 1));
                parent.getNodeList().add(node.getNodeIndex() + 1, newNode);
                // ===================node节点的赋值过程===================
                node.setRightNode(newNode);
            } else {
                //node为非叶子节点,则新生成一个非叶子节点
                Node<Integer, V> newNode = newBranchNode();
                // ===================叶子节点的赋值过程===================
                newNode.setParent(node.getParent());
                newNode.setNodeIndex(node.getNodeIndex() + 1);
                
                for (int i = 0; i < half; i++) {
                    newNode.getEntries().add(0, node.getEntries().get(this.degree - i));
                    node.getEntries().remove(this.degree - i);
                    Node<Integer, V> halfNode = node.getNodeList().get(this.degree - i);
                    halfNode.setParent(newNode);
                    newNode.getNodeList().add(0,node.getNodeList().get(this.degree - i));
                    node.getNodeList().remove(this.degree - i);
                }
                changeRightNodeIndex(newNode.getNodeList().get(0), 0);
                // ===================node.parent节点的赋值过程===================
                Node<Integer, V> parent = node.getParent();
                parent.getEntries().add(node.getNodeIndex(), node.getEntries().get(half - 1));
                parent.getNodeList().add(node.getNodeIndex() + 1,newNode);

                changeRightNodeIndex(newNode, newNode.getNodeIndex());
            }
            if (node.getParent().getEntries().size() > this.degree) {
                splitNode(node.getParent());
            }
        }
    }

    /**
     * 判别rightNode是否是node同一个父节点下的右节点
     * @param node
     * @param rightnode
     * @return
     */
    private boolean isRightNodeInCommonParent(Node<Integer, V> node, Node<Integer, V> rightnode) {
        if (!node.isLeaf()) {
            throw new RuntimeException(node.getClass() + "is not leafNode, can't identify");
        }
        if (node.getNodeIndex() == node.getParent().getNodeList().size() - 1) {
            return false;
        }
        return node.getParent().getNodeList().get(node.getNodeIndex() + 1) == rightnode;
    }

    /**
     * 判别leftNode是否是node同一个父节点下的左节点
     * @param node
     * @param leftnode
     * @return
     */
    private boolean isLeafNodeInCommonParent(Node<Integer, V> node, Node<Integer, V> leftnode) {
        if (!node.isLeaf()) {
            throw new RuntimeException(node.getClass() + "is not leafNode, can't identify");
        }
        if (node.getNodeIndex() == 0) {
            return false;
        }
        return node.getParent().getNodeList().get(node.getNodeIndex() - 1) == leftnode;
    }


    /**
     * 设置node在parent中的nodeIndex,此外在同一个parent下的所有右兄弟节点的nodeIndex都要从startNodeIndex开始依次+1
     * 例如
     *        K0-》index:0    K1-》index:1    K2-》index:2    K3-》index:3
     *        K1节点被删除了,那么再执行subRightNodeIndex(K2, 1)
     *        ===》
     *        K0-》index:0    K2-》index:1    K3-》index:2
     *        也就是说将
     *          k2.nodeIndex = nodeIndex;
     *          K3.nodeIndex = nodeIndex + 1;
     *
     * @param node
     */
    private void changeRightNodeIndex(Node<Integer, V> node, Integer startNodeIndex) {
        List<Node<Integer, V>> parentNodeList = node.getParent().getNodeList();
        int nodeIndex = -1;
        for (int i = 0; i < parentNodeList.size(); i++) {
            if (node == parentNodeList.get(i)) {
                nodeIndex = i;
                break;
            }
        }

        if (nodeIndex == -1) {
            throw new RuntimeException("nodeIndex = -1");
        }

        for (int i = nodeIndex; i < parentNodeList.size(); i++) {
            Node<Integer, V> rightNode = parentNodeList.get(i);
            rightNode.setNodeIndex(startNodeIndex++);
        }
    }

    /**
     * 层序遍历输出
     *                                 9-18-27
     *                     /               |               \
     *         3-6-9                   12-15-18                     21-24-27
     *     /     |     \           /       |       \           /       |       \
     * 1-2-3  4-5-6  7-8-9  10-11-12  13-14-15  16-17-18  19-20-21  22-23-24  25-26-27
     *
     *    print输出
     *    9-18-27
     *    3-6-9  12-15-18  21-24-27
     *    1-2-3  4-5-6  7-8-9  10-11-12  13-14-15  16-17-18  19-20-21  22-23-24  25-26-27
     */
    public void print() {
        Queue<Pair<Node<Integer, V>, Integer>> queue = new LinkedList<>();
        StringBuilder sb = new StringBuilder();
        ArrayList<Pair<Node<Integer, V>, Integer>> list = new ArrayList<>();
        queue.offer(new Pair<>(this.head, 0));
        while (!queue.isEmpty()) {
            Pair<Node<Integer, V>, Integer> pair = queue.poll();
            list.add(pair);
            Node<Integer, V> node = pair.getKey();
            if (!node.isLeaf()) {
                for (int i = 0; i < node.getNodeList().size(); i++) {
                    queue.offer(new Pair<>(node.getNodeList().get(i), pair.getValue() + 1));
                }
            }
        }
        for (int i = 0; i < list.size(); i++) {
            Pair<Node<Integer, V>, Integer> pair = list.get(i);
            List<Entry<Integer, V>> entries = pair.getKey().getEntries();
            if (i > 0) {
                Integer afterVal = list.get(i - 1).getValue();
                Integer val = list.get(i).getValue();
                if (afterVal != val) {
                    sb.append("\r\n");
                } else {
                    sb.append("  ");
                }
            }
            for (int j = 0; j < entries.size(); j++) {
                sb.append(entries.get(j).getKey());
                if (j < entries.size() -1) {
                    sb.append("-");
                }
            }
            //sb.append(":"+pair.getValue());
        }
        System.out.println(sb.toString());
    }


    public void printInfo() {
        Queue<Pair<Node<Integer, V>, Integer>> queue = new LinkedList<>();
        StringBuilder sb = new StringBuilder();
        ArrayList<Pair<Node<Integer, V>, Integer>> list = new ArrayList<>();
        queue.offer(new Pair<>(this.head, 0));
        while (!queue.isEmpty()) {
            Pair<Node<Integer, V>, Integer> pair = queue.poll();
            list.add(pair);
            Node<Integer, V> node = pair.getKey();
            if (!node.isLeaf()) {
                for (int i = 0; i < node.getNodeList().size(); i++) {
                    queue.offer(new Pair<>(node.getNodeList().get(i), pair.getValue() + 1));
                }
            }
        }
        for (int i = 0; i < list.size(); i++) {
            Pair<Node<Integer, V>, Integer> pair = list.get(i);
            List<Entry<Integer, V>> entries = pair.getKey().getEntries();
            if (i > 0) {
                Integer afterVal = list.get(i - 1).getValue();
                Integer val = list.get(i).getValue();
                if (afterVal != val) {
                    sb.append("\r\n");
                } else {
                    sb.append("  ");
                }
            }
            for (int j = 0; j < entries.size(); j++) {
                sb.append(entries.get(j).getKey());
                if (j < entries.size() -1) {
                    sb.append("-");
                }
            }
            Node<Integer, V> node = pair.getKey();
            Node<Integer, V> parent = node.getParent();
            if (parent != null) {
                sb.append("->parent:"+parent.toString().replace("com.thinking.tree.Node",""));

            } else {
                sb.append("->parent:"+parent);

            }
            sb.append(",this:" + node.toString().replace("com.thinking.tree.Node",""));
            sb.append(",index:" + node.getNodeIndex());
            if (node.isLeaf()) {
                if (node.getLeftNode() != null) {
                    sb.append(",left:"+node.getLeftNode().toString().replace("com.thinking.tree.Node",""));
                } else {
                    sb.append(",left:"+node.getLeftNode());
                }
                if (node.getRightNode() != null) {
                    sb.append(",right:" + node.getRightNode().toString().replace("com.thinking.tree.Node",""));
                } else {
                    sb.append(",right:" + node.getRightNode());
                }
            }

        }
        System.out.println(sb.toString());
    }

    /**
     * 依据key删除
     * @param key
     */
    public void deleteKey(Integer key) {
        Node<Integer, V> leafNode = searchNode(key);
        SearchResult search = leafNode.search(key);
        if (!search.isExist()) {
            return;
        } else {
            //需要删除的key在树中存在
            int searchIndex = search.getSearchIndex();
            List<Entry<Integer, V>> entries = leafNode.getEntries();
            entries.remove(searchIndex);
            loopChangeParent(leafNode);
            if (entries.size() >= (this.degree + 1) / 2 || leafNode == head) {
                return;
            }  else {
                //  !(  entries.size() >= (this.degree + 1) / 2 || leafNode == head )
                //  可得 entries.size() < (this.degree + 1) / 2 && leafNode != head
                //  再由 leafNode != head 和B+树的定义(非叶根节点至少有两棵子树)可得,leaf要么存在左节点,要么存在右节点
                //  这个性质后续代码会用到
                borrowAndcombineNode(leafNode);
            }
        }
    }

    /**
     * 从node开始借entry,和合并
     * @param node
     */
    private void borrowAndcombineNode(Node<Integer, V> node) {
        Node<Integer, V> leftNode = getLeftNodeInCommonParent(node);
        Node<Integer, V> rightNode = getRightNodeInCommonParent(node);
        if (node.isLeaf()) {
            if (leftNode != null && leftNode.getEntries().size() > half) {//找左借
                node.getEntries().add(0, leftNode.getEntries().get(leftNode.getEntries().size() - 1));
                leftNode.getEntries().remove(leftNode.getEntries().size() - 1);
                loopChangeParent(leftNode);
            } else if (rightNode != null && rightNode.getEntries().size() > half) {//找右借
                node.getEntries().add(rightNode.getEntries().get(0));
                rightNode.getEntries().remove(0);
                loopChangeParent(node);//右借之后,node.entries的最后一个元素变了
            } else {//左右都不能借,那只能合并了
                combineNode(node);
            }
        }
    }

    /**
     * 得到node同一个parent下的leftNode
     * @param node
     * @return
     */
    private Node<Integer, V> getLeftNodeInCommonParent(Node<Integer, V> node) {
        if (node.getParent() == null || node == node.getParent().getNodeList().get(0)) {
            return null;
        }
        return node.getParent().getNodeList().get(node.getNodeIndex() - 1);
    }

    /**
     * 得到node同一个parent下的rightNode
     * @param node
     * @return
     */
    private Node<Integer, V> getRightNodeInCommonParent(Node<Integer, V> node) {
        if (node.getParent() == null || node == node.getParent().getNodeList().get(node.getParent().getNodeList().size() - 1)) {
            return null;
        }
        return node.getParent().getNodeList().get(node.getNodeIndex() + 1);
    }

    /**
     * 当这个方法执行的时候,一定存在叶子节点的合并,可能存在非叶子节点的合并
     * 节点合并
     *      叶子节点的合并
     *           合并到左节点中(同一个父节点),父节点的entries.size和nodeList.size()都要-1,层数是否降低
     *           合并到右节点中(同一个父节点),父节点的entries.size和nodeList.size()都要-1,层数是否降低
     *      非叶子节点
     *          非叶子节点的不合并(当node.entries.size() >= half)
     *          非叶子节点的合并(当node.entries.size() < half)
     *              合并到左节点中(同一个父节点),父节点的entries.size和nodeList.size()都要-1,层数是否降低
     *              合并到右节点中(同一个父节点),父节点的entries.size和nodeList.size()都要-1,层数是否降低
     * @param node
     */
    private void combineNode(Node<Integer, V> node) {
        if (node.getEntries().size() >= half || node == head) {
            return;
        }
        Node<Integer, V> parent = node.getParent();
        Node<Integer, V> leftNode = getLeftNodeInCommonParent(node);//同一个父节点中的左节点
        Node<Integer, V> rightNode = getRightNodeInCommonParent(node);//同一个父节点中的右节点
        if (node.isLeaf()) {
            Node<Integer, V> leftRealNode = node.getLeftNode();//node的左节点
            Node<Integer, V> rightRealNode = node.getRightNode();//node的右节点
            if (leftNode != null) {//左节点都不为空,元素移动到左节点
                //===================node节点的赋值过程===================
                node.setParent(null);
                for (int i = 0; i < half - 1; i++) {
                    leftNode.getEntries().add(node.getEntries().get(0));
                    node.getEntries().remove(0);
                }
                node.setLeftNode(null);
                node.setRightNode(null);
                //===================parent节点的赋值过程===================
                parent.getEntries().remove(node.getNodeIndex() - 1);
                parent.getNodeList().remove(node.getNodeIndex());
                //===================左右节点的赋值过程===================
                if (rightRealNode != null) {
                    leftNode.setRightNode(rightRealNode);
                    rightRealNode.setLeftNode(leftNode);
                } else {
                    leftRealNode.setRightNode(null);
                }
                if (rightNode != null) {
                    changeRightNodeIndex(rightNode, rightNode.getNodeIndex() -1);
                }
                if (canOrNotReduceLayers(leftNode)) { return; }
            } else{//左节点为空,右节点不为空,元素移动到右节点
                //===================node节点的赋值过程===================
                node.setParent(null);
                for (int i = 0; i < half - 1; i++) {
                    rightNode.getEntries().add(0, node.getEntries().get(node.getEntries().size() - 1));
                    node.getEntries().remove(node.getEntries().size() - 1);
                }
                node.setLeftNode(null);
                node.setRightNode(null);
                //===================parent节点的赋值过程===================
                parent.getEntries().remove(node.getNodeIndex());
                parent.getNodeList().remove(node.getNodeIndex());
                //===================左右节点的赋值过程===================
                if (leftRealNode != null) {
                    rightNode.setLeftNode(leftRealNode);
                    leftRealNode.setRightNode(rightNode);
                } else {
                    rightNode.setLeftNode(null);
                }
                changeRightNodeIndex(rightNode, rightNode.getNodeIndex() - 1);
                if (canOrNotReduceLayers(rightNode)) {
                    return;
                }
            }
        } else {//合并的节点为非叶子节点
            if (leftNode != null && leftNode.getEntries().size() > half) {//从左边借
                leftBorrow(node);
            } else if (rightNode != null && rightNode.getEntries().size() > half) {//从右边借
                rightBorrow(node);
            } else {//两边都不能借,只能合并
                //优先找左边合并
                if (node.getNodeIndex() > 0) {//左旋,和左节点合并
                    leftRotate(node);
                    if(canOrNotReduceLayers(leftNode)) {
                        return;
                    }
                } else {//右旋,和右节点合并
                    rightRotate(node);
                    if(canOrNotReduceLayers(rightNode)) {
                        return;
                    }
                }
            }
        }
        combineNode(parent);
    }

    /**
     * @param node 节点合并完成判断是否需要降层数,每当层数降低之后。说明树结构已经符合B+树定义(已经稳定)
     */
    private boolean canOrNotReduceLayers(Node<Integer, V> node) {
        if (node.getParent().getEntries().size() == 1 && node.getParent() == head) {
            //head的设置
            head.getEntries().remove(0);
            head.getNodeList().remove(0);

            //node的设置
            node.setParent(null);
            node.setNodeIndex(-1);
            if (node.getNodeList().get(0) == null) {
                node.setLeaf(true);
            }
            head = node;
            return true;
        }
        return false;
    }

    /**
     * 右借
     * @param node node.getNodeList().size() < half的节点
     */
    private void leftBorrow(Node<Integer, V> node) {
        Node<Integer, V> parent = node.getParent();
        Node<Integer, V> leftNode = parent.getNodeList().get(node.getNodeIndex() - 1);
        List<Entry<Integer, V>> entries = node.getEntries();

        entries.add(0, leftNode.getEntries().get(leftNode.getEntries().size() - 1));
        leftNode.getEntries().remove(leftNode.getEntries().size() - 1);

        List<Node<Integer, V>> nodeList = node.getNodeList();
        Node<Integer, V> leftNodeLastNode = leftNode.getNodeList().get(leftNode.getNodeList().size() - 1);

        nodeList.add(0, leftNodeLastNode);
        leftNode.getNodeList().remove(leftNode.getNodeList().size() - 1);
        leftNodeLastNode.setParent(node);
        leftNodeLastNode.setNodeIndex(0);
        changeRightNodeIndex(node.getNodeList().get(1), 1);
        loopChangeParent(leftNode);
    }

    /**
     * 左借
     * @param node node.getNodeList().size() < half的节点
     */
    private void rightBorrow(Node<Integer, V> node) {
        Node<Integer, V> parent = node.getParent();
        Node<Integer, V> rightNode = parent.getNodeList().get(node.getNodeIndex() + 1);
        List<Entry<Integer, V>> entries = node.getEntries();

        entries.add(rightNode.getEntries().get(0));
        rightNode.getEntries().remove(0);

        List<Node<Integer, V>> nodeList = node.getNodeList();
        Node<Integer, V> rightNodeFirstNode = rightNode.getNodeList().get(0);

        nodeList.add(rightNodeFirstNode);
        rightNode.getNodeList().remove(0);

        rightNodeFirstNode.setParent(node);
        rightNodeFirstNode.setNodeIndex(nodeList.size() - 1);
        changeRightNodeIndex(rightNode.getNodeList().get(0), 0);
        loopChangeParent(node);
    }

    private boolean isExistRightNodeInCommonParent(Node<Integer, V> node) {
        return node.getNodeIndex() < node.getParent().getNodeList().size() - 1;
    }

    private boolean isExistLeftNodeInCommonParent(Node<Integer, V> node) {
        return node.getNodeIndex() > 0;
    }

    /**
     * 左旋,和左节点合并
     * @param node node.getNodeList().size() < half的节点
     */
    private void leftRotate(Node<Integer, V> node) {
        Node<Integer, V> parent = node.getParent();
        Node<Integer, V> leftNode = parent.getNodeList().get(node.getNodeIndex() - 1);
        Node<Integer, V> rightNode = null;
        if (isExistRightNodeInCommonParent(node)) {
            rightNode = parent.getNodeList().get(node.getNodeIndex() + 1);
        }
        for (int i = 0; i < half - 1; i++) {
            leftNode.getEntries().add(node.getEntries().get(0));
            node.getEntries().remove(0);

            Node<Integer, V> nodeFirstNode = node.getNodeList().get(0);
            nodeFirstNode.setParent(leftNode);
            nodeFirstNode.setNodeIndex(leftNode.getNodeList().get(leftNode.getNodeList().size() - 1).getNodeIndex() + 1);
            leftNode.getNodeList().add(nodeFirstNode);
            node.getNodeList().remove(0);
        }
        if (isExistRightNodeInCommonParent(node)) {
            changeRightNodeIndex(rightNode, rightNode.getNodeIndex() -1);
        }
        node.setParent(null);
        parent.getEntries().remove(node.getNodeIndex() - 1);
        parent.getNodeList().remove(node.getNodeIndex());
        loopChangeParent(leftNode);
    }

    /**
     * 右旋,和右节点合并
     * @param node node.getNodeList().size() < half的节点
     */
    private void rightRotate(Node<Integer, V> node) {
        Node<Integer, V> parent = node.getParent();
        List<Node<Integer, V>> parentNodeList = parent.getNodeList();
        Node<Integer, V> rightNode = parentNodeList.get(node.getNodeIndex() + 1);
//        Node<Integer, V> rightNode = parent.getNodeList().get(node.getNodeIndex() + 1);
        for (int i = 0; i < half - 1; i++) {
            rightNode.getEntries().add(0, node.getEntries().get(node.getEntries().size() - 1));
            node.getEntries().remove(node.getEntries().size() - 1);
            Node<Integer, V> nodeLastNode = node.getNodeList().get(node.getNodeList().size() - 1);
            nodeLastNode.setParent(rightNode);
            //nodeLastNode.setNodeIndex(-1);后面会设置
            rightNode.getNodeList().add(0, nodeLastNode);
            node.getNodeList().remove(node.getNodeList().size() - 1);
        }
        changeRightNodeIndex(rightNode.getNodeList().get(0), 0);

        changeRightNodeIndex(rightNode, rightNode.getNodeIndex() -1);
        node.setParent(null);
        parent.getEntries().remove(node.getNodeIndex());
        parent.getNodeList().remove(node.getNodeIndex());
    }

    public List<V> selectGreaterAndEqual(Integer key){
        Node<Integer, V> leafNode = searchNode(key);
        SearchResult search = leafNode.search(key);
        ArrayList<V> resList = new ArrayList<>();
        if (search.isExist()) {
            int insertIndex = search.getInsertIndex();
            List<Entry<Integer, V>> entries = leafNode.getEntries();
            for (int i = insertIndex; i < entries.size() - 1; i++) {
                resList.add(entries.get(i).getValue());
            }
            while (leafNode.getRightNode() != null) {
                Node<Integer, V> rightNode = leafNode.getRightNode();
                List<Entry<Integer, V>> rightNodeEntries = rightNode.getEntries();
                for (int i = 0; i < rightNodeEntries.size() - 1; i++) {
                    resList.add(rightNodeEntries.get(i).getValue());
                }
                leafNode = rightNode;
            }
        }
        return resList;
    }


    public List<V> selectLessAndEqual(Integer key){
        Node<Integer, V> leafNode = searchNode(key);
        SearchResult search = leafNode.search(key);
        ArrayList<V> resList = new ArrayList<>();
        if (search.isExist()) {
            int insertIndex = search.getInsertIndex();
            List<Entry<Integer, V>> entries = leafNode.getEntries();
            for (int i = insertIndex; i >= 0; i--) {
                resList.add(entries.get(i).getValue());
            }
            while (leafNode.getLeftNode() != null) {
                Node<Integer, V> leftNode = leafNode.getLeftNode();
                List<Entry<Integer, V>> leftNodeEntries = leftNode.getEntries();
                for (int i = leftNodeEntries.size() - 1; i >= 0; i--) {
                    resList.add(leftNodeEntries.get(i).getValue());
                }
                leafNode = leftNode;
            }
        }
        return resList;
    }
}

5)BPTTest
package com.thinking.tree;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class BPTTest {
    public static void main(String[] args) {
//        test1();
//        test2();
//        test3();
//        test4();
//        test5();
//        test6();
//        test7();
//        test8();
//        test9();
        //test10();
        //test11();
        //test12();
        test13();
    }

    private static void test1() {
        BPlusTree<Integer> tree = new BPlusTree<>(5);
        for (int i = 0; i <= 130; i++) {
            System.out.println("插入 "+ i +" 之后: ");
            tree.insert(i,i);
            tree.print();
        }

        for (int i = 0; i <= 130; i++) {
            System.out.println("删除 "+ i +" 之后: ");
            tree.deleteKey(130 - i);
            tree.print();
        }
    }

    private static void test2() {
        BPlusTree<Integer> tree = new BPlusTree<>(5);
        Random random = new Random();
        for (int i = 0; i < 1000; i++) {
            int key = random.nextInt(100);
            System.out.println("No." + i + " 插入 "+ key +" 之后: ");
            tree.insert(key,key);
            tree.print();
        }

        for (int i = 0; i < 1000; i++) {
            int key = random.nextInt(100);
            System.out.println("No." + i + " 删除 "+ key +" 之后: ");
            tree.deleteKey(key);
            tree.print();
        }
    }

    private static void test3() {
        ArrayList<Entry<Integer, Integer>> entries = new ArrayList<>(1000000);
        for (int i = 1; i <= 1000000; i++) {
            entries.add(new Entry<>(i,i));
        }
        long startTime = System.currentTimeMillis();
        for (Entry<Integer, Integer> entry : entries) {
            if (entry.getKey() == 1000000/2) break;
        }
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }

    private static void test4() {
        BPlusTree<Integer> tree = new BPlusTree<>(1000);
        for (int i = 1; i <= 1000000/2; i++) {
            tree.insert(i,i);
        }
        for (int i = 1000000/2 + 2; i <= 1000000; i++) {
            tree.insert(i,i);
        }
        long startTime = System.currentTimeMillis();
        System.out.println(tree.isExist(1000000 / 2 + 1));
        long endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime);
    }

    private static void test5() {
        BPlusTree<Integer> tree = new BPlusTree<>(5);
        for (int i = 1; i <= 60; i++) {
            tree.insert(i,i);
        }
        tree.print();
    }

    private static void test6() {
        BPlusTree<Integer> tree = new BPlusTree<>(5);
        for (int i = 1; i <= 13; i++) {
            tree.insert(i,i);
        }
        tree.print();

        int deleteKeyKey = 8;
        tree.deleteKey(deleteKeyKey);
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.print();
    }

    private static void test7() {
        BPlusTree<Integer> tree = new BPlusTree<>(5);
        for (int i = 0; i <= 130; i += 10) {
            tree.insert(i,i);
        }
        tree.insert(19,19);
        tree.print();

        int deleteKeyKey = 80;
        tree.deleteKey(deleteKeyKey);
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.print();
    }

    private static void test8() {
        BPlusTree<Integer> tree = new BPlusTree<>(5);
        String flag = "print";
        for (int i = 0; i <= 400; i += 10) {
            tree.insert(i,i);
        }
        for (int i = 71; i < 79; i++) {
            tree.insert(i,i);
        }
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }


        int deleteKeyKey = 80;
        tree.deleteKey(deleteKeyKey);
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 78;
        tree.deleteKey(deleteKeyKey);
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 100;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 74;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 0;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

    }

    private static void test9() {
        BPlusTree<Integer> tree = new BPlusTree<>(5);
        int num = 18;
        String flag = "printInfo";
        int deleteKeyKey = -1;
        for (int i = 0; i < num; i++) {
            tree.insert(i, i);
        }

        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 0;
        tree.deleteKey(deleteKeyKey);
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }
    }

    private static void test10() {
        BPlusTree<Integer> tree = new BPlusTree<>(5);
        String flag = "printInfo";
        int deleteKeyKey = -1;
        for (int i = 1; i <= 60; i++) {
            tree.insert(i, i);
        }
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        tree.deleteKey(4);
        tree.deleteKey(2);
        tree.deleteKey(3);
        tree.deleteKey(5);
        tree.deleteKey(1);
        tree.deleteKey(6);

        deleteKeyKey = 260;
        tree.deleteKey(deleteKeyKey);
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }
    }

    private static void test11() {
        BPlusTree<Integer> tree = new BPlusTree<>(5);
        String flag = "print";
        int deleteKeyKey = -1;
        for (int i = 1; i <= 36; i++) {
            tree.insert(i, i);
        }
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 1;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 36;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 2;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 3;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 4;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 35;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 34;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 33;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 5;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 6;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 7;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 30;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 31;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 32;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 8;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 9;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        deleteKeyKey = 10;
        System.out.println("删除 " +deleteKeyKey+" 之后:");
        tree.deleteKey(deleteKeyKey);
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

//        deleteKeyKey = 11;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 12;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 13;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 29;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 28;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 27;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 14;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 15;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 16;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 16;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 26;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 25;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 24;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 17;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 18;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 19;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 23;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 22;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 21;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }
//
//        deleteKeyKey = 20;
//        System.out.println("删除 " +deleteKeyKey+" 之后:");
//        tree.deleteKey(deleteKeyKey);
//        if ("print".equals(flag)) {
//            tree.print();
//        } else {
//            tree.printInfo();
//        }

    }

    private static void test12() {
        BPlusTree<Integer> tree = new BPlusTree<>(5);
        String flag = "print";
        int deleteKeyKey = -1;
        for (int i = 1; i <= 36; i++) {
            tree.insert(i, i);
        }
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

//        List<Integer> list = tree.selectGreaterAndEqual(10);
//        StringBuilder sb = new StringBuilder();
//        for (Integer i : list) {
//            sb.append(i).append(" ");
//        }
//        System.out.println(sb.toString());

        List<Integer> list = tree.selectLessAndEqual(10);
        StringBuilder sb = new StringBuilder();
        for (Integer i : list) {
            sb.append(i).append(" ");
        }
        System.out.println(sb.toString());
    }

    private static void test13() {
        BPlusTree<Integer> tree = new BPlusTree<>(5);
        String flag = "print";
        for (int i = 1; i <= 36; i++) {
            tree.insert(i);
        }
        if ("print".equals(flag)) {
            tree.print();
        } else {
            tree.printInfo();
        }

        for (int i = 0; i <= 36; i++) {
            System.out.println("删除 " + i +" 之后:");
            tree.delete(i);
            if ("print".equals(flag)) {
                tree.print();
            } else {
                tree.printInfo();
            }
        }
    }
}

8.结语

距离上次写B树已经过去1年了,最近花了10天写完这东西,基本上就是不停画图,腰酸背疼的。如果对你有用,那我也挺高兴的,如果能帮我点个赞,那我更高兴了。写完这篇,我也要忙着换工作了,下次写不知道是啥时候了。

感谢以下博客和软件:
王道–数据结构(bilibili视频)
B+树及插入和删除操作详解
聊聊MongoDB(一):MongoDB体系结构与底层原理
画图软件drawio

  • 16
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据库中的B树和B树都是一种用于组织和管理数据的树形数据结构。它们都是为了解决在大规模数据存储和高效检索方面的问题。 B树是一种自平衡的搜索树,旨在通过减少I/O操作的数量来提高数据访问的效率。它的特点是具有多个子节点的节点和一个特定的阶数,通常用于磁盘或其他外部存储设备上的数据组织。B树通过在节点中存储多个关键字和相应的指针来实现高效的查找和插入操作。每个节点可以容纳更多的关键字,这样就减少了I/O操作的次数,提高了性能。 B 树是B树的一种变体,也被称为平衡多路搜索树。它也是一种自平衡的搜索树,常用于内存中的数据组织。B 树的特点是每个节点有更多的子节点,并且可以容纳更多的关键字。相比于B树,B 树更适合在内存中进行操作,因为它可以减少树的高度,从而减少了访问数据的时间。 在实际应用中,B树和B 树被广泛应用于数据库系统中的索引结构。通过使用B树和B 树作为索引,可以加快数据库的查询速度,提高数据的访问效率。另外,它们还可以应用于文件系统、哈希表等数据结构的实现中。 总结起来,B树和B 树都是在数据库中常见的用于组织和管理数据的树形数据结构,它们通过自平衡和优化节点的存储方式来提高数据的访问效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [B树和B+树详解](https://blog.csdn.net/qq_33905217/article/details/121827393)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值