B+树 -- Java实现

本文详细介绍了B+树的定义、特点及其插入与删除操作的过程。并提供了完整的Java代码实现,帮助读者深入理解B+树的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、B+树定义

B+树定义:关键字个数比孩子结点个数小1的树。

除此之外B+树还有以下的要求:

  1. B+树包含2种类型的结点:内部结点(也称索引结点)和叶子结点。根结点本身即可以是内部结点,也可以是叶子结点。根结点的关键字个数最少可以只有1个。

  2. B+树与B树最大的不同是内部结点不保存数据,只用于索引,所有数据(或者说记录)都保存在叶子结点中。

  3. m阶B+树表示了内部结点最多有m-1个关键字(或者说内部结点最多有m个子树),阶数m同时限制了叶子结点最多存储m-1个记录。

  4. 内部结点中的key都按照从小到大的顺序排列,对于内部结点中的一个key,左树中的所有key都小于它,右子树中的key都大于等于它。叶子结点中的记录也按照key的大小排列。

  5. 每个叶子结点都存有相邻叶子结点的指针,叶子结点本身依关键字的大小自小而大顺序链接。

二、B+树的插入操作

1)若为空树,创建一个叶子结点,然后将记录插入其中,此时这个叶子结点也是根结点,插入操作结束。

2)针对叶子类型结点:根据key值找到叶子结点,向这个叶子结点插入记录。插入后,若当前结点key的个数小于等于m-1,则插入结束。否则将这个叶子结点分裂成左右两个叶子结点,左叶子结点包含前m/2个记录,右结点包含剩下的记录,将第m/2+1个记录的key进位到父结点中(父结点一定是索引类型结点),进位到父结点的key左孩子指针向左结点,右孩子指针向右结点。将当前结点的指针指向父结点,然后执行第3步。

3)针对索引类型结点:若当前结点key的个数小于等于m-1,则插入结束。否则,将这个索引类型结点分裂成两个索引结点,左索引结点包含前(m-1)/2个key,右结点包含m-(m-1)/2个key,将第m/2个key进位到父结点中,进位到父结点的key左孩子指向左结点, 进位到父结点的key右孩子指向右结点。将当前结点的指针指向父结点,然后重复第3步。

下面是一颗5阶B树的插入过程,5阶B数的结点最少2个key,最多4个key。

a)空树中插入5

image

b)依次插入8,10,15

image

c)插入16

image

插入16后超过了关键字的个数限制,所以要进行分裂。在叶子结点分裂时,分裂出来的左结点2个记录,右边3个记录,中间key成为索引结点中的key,分裂后当前结点指向了父结点(根结点)。结果如下图所示。

image

当然我们还有另一种分裂方式,给左结点3个记录,右结点2个记录,此时索引结点中的key就变为15。

d)插入17
image

e)插入18,插入后如下图所示

image

当前结点的关键字个数大于5,进行分裂。分裂成两个结点,左结点2个记录,右结点3个记录,关键字16进位到父结点(索引类型)中,将当前结点的指针指向父结点。

image

当前结点的关键字个数满足条件,插入结束

f)插入若干数据后

image

g)在上图中插入7,结果如下图所示

image

当前结点的关键字个数超过4,需要分裂。左结点2个记录,右结点3个记录。分裂后关键字7进入到父结点中,将当前结点的指针指向父结点,结果如下图所示。

image

当前结点的关键字个数超过4,需要继续分裂。左结点2个关键字,右结点2个关键字,关键字16进入到父结点中,将当前结点指向父结点,结果如下图所示。

image

当前结点的关键字个数满足条件,插入结束。

三、B+树的删除操作

如果叶子结点中没有相应的key,则删除失败。否则执行下面的步骤

1)删除叶子结点中对应的key。删除后若结点的key的个数大于等于Math.ceil(m-1)/2 – 1,删除操作结束,否则执行第2步。

2)若兄弟结点key有富余(大于Math.ceil(m-1)/2 – 1),向兄弟结点借一个记录,同时用借到的key替换父结(指当前结点和兄弟结点共同的父结点)点中的key,删除结束。否则执行第3步。

3)若兄弟结点中没有富余的key,则当前结点和兄弟结点合并成一个新的叶子结点,并删除父结点中的key(父结点中的这个key两边的孩子指针就变成了一个指针,正好指向这个新的叶子结点),将当前结点指向父结点(必为索引结点),执行第4步(第4步以后的操作和B树就完全一样了,主要是为了更新索引结点)。

4)若索引结点的key的个数大于等于Math.ceil(m-1)/2 – 1,则删除操作结束。否则执行第5步

5)若兄弟结点有富余,父结点key下移,兄弟结点key上移,删除结束。否则执行第6步

6)当前结点和兄弟结点及父结点下移key合并成一个新的结点。将当前结点指向父结点,重复第4步。

注意,通过B+树的删除操作后,索引结点中存在的key,不一定在叶子结点中存在对应的记录。

下面是一颗5阶B树的删除过程,5阶B数的结点最少2个key,最多4个key。

a)初始状态

image

b)删除22,删除后结果如下图

image

删除后叶子结点中key的个数大于等于2,删除结束

c)删除15,删除后的结果如下图所示

image

删除后当前结点只有一个key,不满足条件,而兄弟结点有三个key,可以从兄弟结点借一个关键字为9的记录,同时更新将父结点中的关键字由10也变为9,删除结束。

image

d)删除7,删除后的结果如下图所示
image

当前结点关键字个数小于2,(左)兄弟结点中的也没有富余的关键字(当前结点还有个右兄弟,不过选择任意一个进行分析就可以了,这里我们选择了左边的),所以当前结点和兄弟结点合并,并删除父结点中的key,当前结点指向父结点。

image

此时当前结点的关键字个数小于2,兄弟结点的关键字也没有富余,所以父结点中的关键字下移,和两个孩子结点合并,结果如下图所示。

image

四、Java代码实现

public class BTreeNode {

    public BTreeNode parent;//父节点

    /*以升序方式存储.*/
    public List<Integer> keys;
    /*孩子*/
    public List<BTreeNode> children;

    public boolean leaf;//是否是子节点

    /*子节点中指向下一个节点.*/
    public BTreeNode next;

    public BTreeNode() {
        keys = new ArrayList<>();
        children = new ArrayList<>();
        leaf = false;
    }

    /*返回关键字个数*/
    public int size() {
        return keys.size();
    }

    //该节点中存储的索引是否包含该key值,包含则返回当前索引值,否则返回小于key值的索引
    public SearchResult searchKey(Integer key) {
        int index = Collections.binarySearch(keys, key);
        if (index >= 0) {
            return new SearchResult(index, true);
        } else {
            return new SearchResult(Math.abs(index + 1), false);
        }
    }

    //keys集合是升序排序,这里做了排序的动作,可以直接添加,然后对集合重新排序
    public void addKey(Integer key) {
        SearchResult searchResult = searchKey(key);
        if (!searchResult.found) {
            List<Integer> list = new ArrayList<>(size() + 1);
            for (int i = 0; i < searchResult.index; i++) {
                list.add(keys.get(i));
            }
            list.add(key);
            for (int i = searchResult.index; i < keys.size(); i++) {
                list.add(keys.get(i));
            }
            keys = list;
        }
    }

    //从集合中移除索引
    public void removeKey(Integer key) {
        keys.remove(key);
    }

    //获取子孩子
    public BTreeNode childAt(int index) {
        if (leaf) {
            throw new UnsupportedOperationException("Leaf node doesn't have children.");
        } else {
            return children.get(index);
        }
    }
    
    //将子孩子添加到集合末尾
    public void addChild(BTreeNode node) {
        children.add(node);
    }

    public void removeChild(int index) {
        children.remove(index);
    }

    //某个位置上的子孩子添加
    public void addChild(BTreeNode child, int index) {
        List<BTreeNode> newChildren = new ArrayList<>();
        int i = 0;
        for (; i < index; ++i) {
            newChildren.add(children.get(i));
        }
        newChildren.add(child);
        for (; i < children.size(); ++i) {
            newChildren.add(children.get(i));
        }
        children = newChildren;
    }
}

public class SearchResult {

    public int index;//索引所在集合的位置

    public boolean found;//是否找到索引

    public SearchResult() {

    }

    public SearchResult(int index, boolean found) {
        this.index = index;
        this.found = found;
    }
}

public class Result extends SearchResult {

    public BTreeNode node;//当前节点,索引值没有找到,则为要插入的节点
    
    public int parentIndex;//当前节点在父级节点的位置
     
    public Result(BTreeNode node, int index, int parentIndex, boolean found) {
        super(index, found);
        this.node = node;
        this.parentIndex = parentIndex;
    }

    public Result(BTreeNode node, int parentIndex, SearchResult searchResult) {
        super(searchResult.index, searchResult.found);
        this.node = node;
        this.parentIndex = parentIndex;
    }
}

上面的基本定义则描述了一个节点包括其索引集合,还包括其子孩子,并且在BTreeNode中封装了一些方法,供后续调用

public class BTree {

    private static final int DEFAULT_T = 2;
    public BTreeNode root;
    /* 根据B树的定义,B树的每个非根节点的关键字数n满足(t - 1) <= n <= (2t - 1) */
    private int t = DEFAULT_T;
    /* 非根节点中最小的键值数 */
    private int minKeySize;
    /* 非根节点中最大的键值数 */
    private int maxKeySize;

    public BTree(int t /*传入b树的阶数*/) {
        this();
        this.t = t;
        minKeySize = t / 2;
        maxKeySize = t - 1;
    }
}

封装方法,找到当前索引的位置,没有找到时,则返回索引所在的节点中集合的位置,

public Result searchLeafNode(BTreeNode node, int parentIndex, Integer key) {
        SearchResult searchResult = node.searchKey(key);
        if (node.leaf) {//子节点
            return new Result(node, parentIndex, searchResult);
        } else {
            if (searchResult.found) {
                searchResult.index++;
            }
            return searchLeafNode(node.children.get(searchResult.index), searchResult.index, key);
        }
    }

插入思路:

  1. 找到关键字的位置,找到该节点一定是子节点。
  2. 添加了关键字的节点,判断是否满了,满了则进行分裂
  3. 子节点分裂时,选取中间值上升为父节点中值,但不从子节点中移除,因为子节点保存关键字的值,非子节点保存仅仅是索引
    public boolean insert(Integer key) {
        // 找到子节点
        Result result = searchLeafNode(root, 0, key);
        if (result.found) {//找到该节点,不操作
            return false;
        }
        BTreeNode node = result.node;
        node.addKey(key);
        //判断节点是否满了,满了则进行分割
        if (isFull(node)) {
            split(node.parent, result.parentIndex, node);
        }
        return true;
    }
    
    //进行分割
    public void split(BTreeNode parentNode, int parentIndex, BTreeNode childNode) {
        //将当前节点一份为二,小的部分将放入到新节点中,自身则成为右节点
        int mid = childNode.size() / 2;
        Integer key = null;
        boolean unLeaf = childNode.children.isEmpty();
        //判断是否为子节点,如果是子节点,索引会放入到右节点中,否则会放入到父节点中
        if (unLeaf) {
            key = childNode.keys.get(mid);
        } else {
            key = childNode.keys.remove(mid);
        }
        //分裂出左节点形成新的节点
        List<Integer> keys = new ArrayList<>();
        for (int i = 0; i < mid; i++) {
            Integer k = childNode.keys.remove(0);
            keys.add(k);
        }
        BTreeNode node = new BTreeNode();
        node.parent = parentNode;
        node.leaf = childNode.children.isEmpty();
        node.keys.addAll(keys);
        node.next = childNode;//节点下一个
        //将孩子节点部分也移动到新节点中
        if (!unLeaf) {
            mid = childNode.children.size() / 2;
            for (int i = 0; i < mid; i++) {
                BTreeNode bTreeNode = childNode.children.remove(0);
                bTreeNode.parent = node;
                node.addChild(bTreeNode);
            }
        }
        //父节点为空时,需要产生一个新节点
        if (parentNode == null) {
            root = new BTreeNode();
            root.leaf = false;
            parentNode = root;
            childNode.parent = parentNode;
            node.parent = parentNode;
            parentNode.children.add(childNode);
        }

        int index = parentNode.addKey(key);
        //前一个指针的下一个指针重新定向
        BTreeNode preNode = parentNode.children.get(parentIndex);
        preNode.next = node;
        //将节点添加到列表中
        parentNode.addChild(node, index);
        if (isFull(parentNode)) {//父节点终索引是否满了,满了,则继续分裂
            split(parentNode.parent, 0, parentNode);
        }
    }
    
    private boolean isFull(BTreeNode node) {
        return node.size() > maxKeySize;
    }

删除关键字思路:

  1. 找到该节点,如果是为找到,直接返回
  2. 找到该节点,出现一定是在子节点中,移除掉后,判断子节点的索引值是否大于最小数,大于则返回,否则需要进行合并。移除的当前节点如果出现在父节点中,也需要移除。会从子节点中选择一个节点进行补充
  3. 删除后小于最小数,则先从兄弟节点借,如果兄弟节点借不出,则进行合并
  4. 子节点进行合并,不需要移动子孩子
  5. 父节点进行合并,需要将孩子节点移动
    //删除节点
    public boolean delete(Integer key) {
        //找到该节点
        Result result = searchLeafNode(root, 0, key);
        if (!result.found) {//未找到
            return false;
        }
        //删除的节点数量大于
        BTreeNode node = result.node;
        node.removeKey(key);
        if (node.keys.size() >= minKeySize) {
            if (node.parent.keys.contains(key)) {//父节点中包含该节点
                Integer min = node.keys.get(0);
                node.parent.removeKey(key);
                node.parent.addKey(min);
            }
            return true;
        }
        //删除节点后,不满足情况,则找兄弟节点借
        BTreeNode parent = node.parent;
        if (result.parentIndex != 0 && parent.children.get(result.parentIndex - 1).keys.size() > minKeySize) {//左节点有富余可以借
            BTreeNode left = parent.children.get(result.parentIndex - 1);
            Integer max = left.keys.remove(left.keys.size() - 1);
            //替换节点
            if (parent.keys.contains(key)) {
                parent.removeKey(key);
                parent.addKey(max);
                node.addKey(max);
            } else {
                Integer min = node.keys.get(0);
                parent.removeKey(min);
                parent.addKey(max);
                node.addKey(max);
            }
        } else if (result.parentIndex < parent.children.size() - 1 && parent.children.get(result.parentIndex - 1).keys.size() > minKeySize) {//右节点有富余可以借
            BTreeNode right = parent.children.get(result.parentIndex + 1);
            Integer min = right.keys.remove(0);
            //替换节点
            if (parent.keys.contains(key)) {
                parent.removeKey(key);
                parent.addKey(min);
                node.addKey(min);
            } else {
                Integer max = node.keys.get(node.keys.size() - 1);
                parent.removeKey(max);
                parent.addKey(min);
                node.addKey(min);
            }
        } else {
            //兄弟节点也没有,则进行合并
            node.parent.removeKey(key);
            node.parent.removeKey(node.keys.get(0));
            union(node, result.parentIndex);
        }
        return true;
    }

    public void union(BTreeNode node, int parentIndex) {
        int ch = 0;
        if (parentIndex == 0) {//当前节点是最左节点,则只能找右节点
            ch = 1;
        } else {//否则找左节点
            ch = parentIndex - 1;
        }
        BTreeNode parent = node.parent;
        if (parent == null) {
            return;
        }
        BTreeNode kNode = parent.children.get(ch);
        for (int i = 0; i < node.size(); i++) {
            kNode.addKey(node.keys.get(i));
        }
        parent.removeChild(parentIndex);//移除节点
        //判断上级节点
        if (parent.keys.size() < minKeySize) {
            union(parent);
        }
    }


    public void union(BTreeNode node) {
        if (node.parent == null) {
            return;
        }
        Integer min = node.keys.get(0);
        BTreeNode parent = node.parent;
        //找到当前节点的位置
        Integer index = -1;
        for (int i = parent.keys.size() - 1; i >= 0; i--) {
            if (min > parent.keys.get(i)) {
                index = i;
                break;
            }
        }
        Integer parentValue = null;
        if (index != -1) {
            parentValue = parent.keys.get(index);
        } else {//没有找到则表示当前节点为最左节点
            parentValue = parent.keys.get(index + 1);
        }
        if (index != -1 && parent.children.get(index).keys.size() > minKeySize) {
            //判断左节点是否富余
            BTreeNode left = parent.children.get(index);
            Integer max = left.keys.get(left.size() - 1);
            parent.keys.add(index, max);
            node.addKey(parentValue);
            node.addChild(left.children.get(left.children.size() - 1), 0);
        } else if ((index == -1 && parent.children.get(index + 2).keys.size() > minKeySize) || (index < parent.keys.size() - 1 && parent.children.get(index + 1).keys.size() > minKeySize)) {
            //判断右节点是否富余
            BTreeNode right = parent.children.get(index + 1);
            Integer m = right.keys.get(0);
            parent.keys.add(index, m);
            node.addKey(parentValue);
            node.addChild(right.children.get(0));
        } else {
            //合并
            if (index == -1) {
                //合并到右节点
                BTreeNode right = parent.children.get(index + 2);
                Integer pa = parent.keys.remove(index + 1);
                right.addKey(pa);
                for (int i = 0; i < node.keys.size(); i++) {
                    right.addKey(node.keys.get(i));
                }
                List<BTreeNode> bTreeNodes = new ArrayList<>();
                for (int i = 0; i < node.children.size(); i++) {
                    bTreeNodes.add(node.children.get(i));
                }
                for (int i = 0; i < right.children.size(); i++) {
                    bTreeNodes.add(right.children.get(i));
                }
                right.children = bTreeNodes;
                parent.children.remove(index + 1);//移除该节点
                if (parent.keys.isEmpty()) {//节点为空
                    root = right;
                    return;
                }
            } else {
                //合并到左节点
                //找到父级节点下沉,并将该节点所有添加到左节点中
                BTreeNode left = parent.children.get(index);
                Integer max = parent.keys.remove(index.intValue());
                left.addKey(max);
                for (int i = 0; i < node.keys.size(); i++) {
                    left.addKey(node.keys.get(i));
                }
                for (int i = 0; i < node.children.size(); i++) {
                    left.children.add(node.children.get(i));
                }
                parent.children.remove(index + 1);//移除该节点
                if (parent.keys.isEmpty()) {//节点为空
                    root = left;
                    return;
                }
            }
        }
        //判断上级节点
        if (parent.keys.size() < minKeySize) {
            union(parent);
        }
    }

打印输出:

private void outPut(BTreeNode node, int index) {
        if (node.leaf) {
            List<Integer> kes = node.keys;
            System.out.println("叶子节点,层级:" + index + ",keys:" + kes);
        } else {
            List<Integer> kes = node.keys;
            System.out.println("层级:" + index + ",keys:" + kes);
            for (int i = 0; i < node.children.size(); i++) {
                outPut(node.children.get(i), index + 1);
            }
        }
    }

    public static void main(String[] args) {
        BTree tree = new BTree(5);
        tree.insert(5);
        tree.insert(8);
        tree.insert(10);
        tree.insert(15);
        tree.insert(16);
        tree.insert(17);
        tree.insert(6);
        tree.insert(9);
        tree.insert(18);
        tree.insert(19);
        tree.insert(20);
        tree.insert(21);
        tree.insert(22);
        tree.insert(7);
        tree.outPut(tree.root, 0);

        System.out.println("---------------------------------------------");

        tree.delete(22);
        tree.delete(15);
        tree.outPut(tree.root, 0);
        System.out.println("---------------------------------------------");
        tree.delete(7);
        tree.outPut(tree.root, 0);
        System.out.println("---------------------------------------------");
    }

最后的结果:

层级:0,keys:[16]
层级:1,keys:[7, 10]
叶子节点,层级:2,keys:[5, 6]
叶子节点,层级:2,keys:[7, 8, 9]
叶子节点,层级:2,keys:[10, 15]
层级:1,keys:[18, 20]
叶子节点,层级:2,keys:[16, 17]
叶子节点,层级:2,keys:[18, 19]
叶子节点,层级:2,keys:[20, 21, 22]
---------------------------------------------
层级:0,keys:[16]
层级:1,keys:[7, 9]
叶子节点,层级:2,keys:[5, 6]
叶子节点,层级:2,keys:[7, 8]
叶子节点,层级:2,keys:[9, 10]
层级:1,keys:[18, 20]
叶子节点,层级:2,keys:[16, 17]
叶子节点,层级:2,keys:[18, 19]
叶子节点,层级:2,keys:[20, 21]
---------------------------------------------
层级:0,keys:[9, 16, 18, 20]
叶子节点,层级:1,keys:[5, 6, 8]
叶子节点,层级:1,keys:[9, 10]
叶子节点,层级:1,keys:[16, 17]
叶子节点,层级:1,keys:[18, 19]
叶子节点,层级:1,keys:[20, 21]

转载于:https://www.cnblogs.com/skyice/p/10624876.html

BPlusTree_Java实现 package bplustree; import java.util.*; import com.xuedi.IO.*; import com.xuedi.maths.*; ////// DisposeRoot ///////中的key参数有些问题 public class BTree { //用于记录每个节点中的键值数量 public int keyAmount; //的根节点 public Node root; public BTree(int keyAmount) { this.keyAmount = keyAmount; this.root = new Node(keyAmount); } //在B中插入叶节点///////////////////////////////////////////////////////////// public void insert(long key,Object pointer) { //找到应该插入的节点 Node theNode = search(key,root); //在叶节点中找到空闲空间,有的话就把键放在那里 if( !isFull(theNode) ) { putKeyToNode(key,pointer,theNode); }else{ //如果在适当的叶节点没有空间,就把该叶节点分裂成两个,并正确分配键值 Node newNode = separateLeaf(key,pointer,theNode); //如果分裂的是根节点,就新建一个新的根节点将新建的节点作为他的字节点 if( isRoot(theNode) ) { DisposeRoot(theNode,newNode,newNode.keys[0]); }else{ //将新建立的节点的指针插入到上层节点 insertToInnerNode(theNode.parent,newNode,newNode.keys[0]); } } } //lowerNode是下级节点分离后新建立的那个节点/////////////////////////////////////// //upperNode是lowerNode的上层节点 private void insertToInnerNode(Node upperNode,Node lowerNode,long key) { //上层节点有空位就直接插入 if( !isFull(upperNode) ) { putKeyToNode(key,lowerNode,upperNode); //重置父节点指针 pointerRedirect(upperNode); return; }else{ //如果分裂的是根节点,就新建一个新的根节点将新建的节点作为他的子节点 Node newNode; if( isRoot(upperNode) ) { newNode = separateInnerNode(key,lowerNode,upperNode); Node newRoot = new Node(this.keyAmount); newRoot.pointer[0] = upperNode; newRoot.pointer[1] = newNode; upperNode.parent = newRoot; newNode.parent = newRoot; newRoot.keyAmount = 1; newRoot.keys[0] = key; root = newRoot; //重置父节点指针 pointerRedirect(upperNode); return; }else{ //上层非根节点没有空位进行分裂和插入操作 newNode = separateInnerNode(key,lowerNode,upperNode); //重置父节点指针 pointerRedirect(upperNode); //记录要向上插入的键值在源节点中的位置(该键值在separateInnerNode()被保留在srcNode中) int keyToUpperNodePosition = upperNode.keyAmount; //向上递归插入 insertToInnerNode(upperNode.parent,newNode,upperNode.keys[keyToUpperNodePosition]); //重置父节点指针 pointerRedirect(newNode); } } } //将对应的内部节点进行分裂并正确分配键值,返回新建的节点 private Node separateInnerNode(long key,Object pointer,Node srcNode) { Node newNode = new Node(this.keyAmount); //因为我在Node中预制了一个位置用于插入,而下面的函数(putKeyToLeaf())不进行越界检查 //所以可以将键-指针对先插入到元节点,然后再分别放到两个节点中 putKeyToNode(key,pointer,srcNode); //先前节点后来因该有(n+1)/2取上界个键-值针对 int ptrSaveAmount = (int)com.xuedi.maths.NumericalBound.getBound(0,(double)(this.keyAmount+1)/2); int keySaveAmount = (int)com.xuedi.maths.NumericalBound.getBound(0,(double)(this.keyAmount)/2); int keyMoveAmount = (int)com.xuedi.maths.NumericalBound.getBound(1,(double)(this.keyAmount)/2); //(n+1)/2取上界个指针和n/2取上界个键留在源节点中 //剩下的n+1)/2取下界个指n/2取下界个键留在源节点中 for (int k = ptrSaveAmount; k < srcNode.keyAmount; k++) { newNode.add(srcNode.keys[k], srcNode.pointer[k]); } newNode.pointer[newNode.keyAmount] = srcNode.pointer[srcNode.pointer.length-1]; srcNode.keyAmount = keySaveAmount; return newNode; } //将对应的叶节点进行分裂并正确分配键值,返回新建的节点/////////////////////////////// private Node separateLeaf(long key,Object pointer,Node srcNode) { Node newNode = new Node(this.keyAmount); //兄弟间的指针传递 newNode.pointer[this.keyAmount] = srcNode.pointer[this.keyAmount]; //因为我在Node中预制了一个位置用于插入,而下面的函数(putKeyToLeaf())不进行越界检查 //所以可以将键-指针对先插入到元节点,然后再分别放到两个节点中 putKeyToNode(key,pointer,srcNode); //先前节点后来因该有(n+1)/2取上界个键-值针对 int oldNodeSize = (int)com.xuedi.maths.NumericalBound.getBound(0,(double)(this.keyAmount+1)/2); for(int k = oldNodeSize; k <= this.keyAmount; k++) { newNode.add(srcNode.keys[k],srcNode.pointer[k]); } srcNode.keyAmount = oldNodeSize; //更改指针--让新节点成为就节点的右边的兄弟 srcNode.pointer[this.keyAmount] = newNode; return newNode; } //把键值放到叶节点中--这个函数不进行越界检查//////////////////////////////////////// private void putKeyToNode(long key,Object pointer,Node theNode) { int position = getInsertPosition(key,theNode); //进行搬迁动作--------叶节点的搬迁 if( isLeaf(theNode) ) { if(theNode.keyAmount <= position) { theNode.add(key,pointer); return; } else{ for (int j = theNode.keyAmount - 1; j >= position; j--) { theNode.keys[j + 1] = theNode.keys[j]; theNode.pointer[j + 1] = theNode.pointer[j]; } theNode.keys[position] = key; theNode.pointer[position] = pointer; } }else{ //内部节点的搬迁----有一定的插入策略: //指针的插入比数据的插入多出一位 for (int j = theNode.keyAmount - 1; j >= position; j--) { theNode.keys[j + 1] = theNode.keys[j]; theNode.pointer[j + 2] = theNode.pointer[j+1]; } theNode.keys[position] = key; theNode.pointer[position+1] = pointer; } //键值数量加1 theNode.keyAmount++; } //获得正确的插入位置 private int getInsertPosition(long key,Node node) { //将数据插入到相应的位置 int position = 0; for (int i = 0; i < node.keyAmount; i++) { if (node.keys[i] > key) break; position++; } return position; } //有用的辅助函数//////////////////////////////////////////////////////////////// //判断某个结点是否已经装满了 private boolean isFull(Node node) { if(node.keyAmount >= this.keyAmount) return true; else return false; } //判断某个节点是否是叶子结点 private boolean isLeaf(Node node) { //int i = 0; if(node.keyAmount == 0) return true; //如果向下的指针是Node型,则肯定不是叶子节点 if(node.pointer[0] instanceof Node) return false; return true; } private boolean isRoot(Node node) { if( node.equals(this.root) ) return true; return false; } //给内部节点中的自己点重新定向自己的父亲 private void pointerRedirect(Node node) { for(int i = 0; i <= node.keyAmount; i++) { ((Node)node.pointer[i]).parent = node; } } //新建一个新的根节点将新建的节点作为他的字节点 private void DisposeRoot(Node child1,Node child2,long key) { Node newRoot = new Node(this.keyAmount); newRoot.pointer[0] = child1; newRoot.pointer[1] = child2; newRoot.keyAmount = 1; newRoot.keys[0] = key; root = newRoot; //如果两个孩子是叶节点就让他们两个相连接 if( isLeaf(child1) ) { //兄弟间的指针传递 child2.pointer[this.keyAmount] = child1.pointer[this.keyAmount]; child1.pointer[this.keyAmount] = child2; } pointerRedirect(root); return; } /////////////////////////////////////////////////////////////////////////////// //用于寻找键值key所在的或key应该插入的节点 //key为键值,curNode为当前节点--一般从root节点开始 public Node search(long key,Node curNode) { if (isLeaf(curNode)) return curNode; for (int i = 0; i < this.keyAmount; i++) { if (key < curNode.keys[i]) //判断是否是第一个值 return search(key, (Node) curNode.pointer[i]); else if (key >= curNode.keys[i]) { if (i == curNode.keyAmount - 1) //如果后面没有值 { //如果key比最后一个键值大,则给出最后一个指针进行递归查询 return search(key,(Node) curNode.pointer[curNode.keyAmount]); } else { if (key < curNode.keys[i + 1]) return search(key, (Node) curNode.pointer[i + 1]); } } } //永远也不会到达这里 return null; } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值