数据结构和算法-bTRee

package com.zwz.BTREE;

import java.util.Arrays;

/**
 * B-tree
 * 度 degree 节点的子节点数量
 * 阶 order 所有节点中子节点最大值
 */
public class BTree {

    static class Node {
        //关键字
        int[] keys;
        //子节点
        Node[] children;
        //有效关键字数目
        int keyNumber;
        //是否是叶子节点
        Boolean leaf = true;
        //最小度数  孩子最少数量
        int t;

        /**
         * 最小度数
         * >=2
         *
         * @param t
         */
        public Node(int t) {
            this.t = t;
            this.children = new Node[t << 1]; // 最多数量 t*2
            this.keys = new int[(t << 1) - 1];
        }

        @Override
        public String toString() {
            return Arrays.toString(Arrays.copyOfRange(keys, 0, keyNumber));
        }

        /**
         * 多路查找
         */
        Node get(int key) {
            int i = 0;
            //查找
            while (i < keyNumber) {
                if (keys[i] > key) {
                    break;
                }
                if (keys[i] == key) {
                    return this;
                }
                i++;
            }
            //叶子节点
            if (leaf) {
                return null;
            } else {
                //到子树查找
                return children[i].get(key);
            }
        }

        //向keys的指定索引index位置插入一个元素
        //1 3 4  index = 1
        // keys 1 keys 2 3
        void insertKey(int key, int index) {
//            for(int i = keyNumber-1;i>=index;i--){
//                keys[i+1] = keys[i];
//            }
            System.arraycopy(keys, index, keys, index + 1, keyNumber - index);
            keys[index] = key;
            keyNumber++;
        }

        //向children 指定索引index 处插入children
        void insertChildren(Node child, int index) {
            System.arraycopy(children, index, children, index + 1, keyNumber - index);
            children[index] = child;
        }

        //删除的工具方法-----------------------------------------

        /**
         * 移除指定index处的key
         */
        int removeKey(int index) {
            int t = keys[index];
            System.arraycopy(keys, index + 1, keys, index, --keyNumber - index);
            return t;
        }

        /**
         * 移除最左边的key
         */
        int removeLeft() {
            return removeKey(0);
        }

        /**
         * 移除最右边的key
         */
        int removeRight() {
            return removeKey(keyNumber - 1);
        }

        /**
         * 移除index处的child
         *
         * @param index
         * @return
         */
        Node removeChild(int index) {
            Node t = children[index];
            System.arraycopy(children, index + 1, children, index, --keyNumber - index);
            return t;
        }

        /**
         * 移除最左边的child
         */
        Node removeLeftChild() {
            return removeChild(0);
        }

        /**
         * 移除最右边的child
         */
        Node removeRightChild() {
            return removeChild(keyNumber);
        }

        /**
         * 获取孩子左边的兄弟
         */
        Node childLeftSibling(int index) {
            return index > 0 ? children[index - 1] : null;
        }

        /**
         * 获取孩子右边的兄弟
         */
        Node childRightSibling(int index) {
            return index == keyNumber ? null : children[index + 1];
        }

        /**
         * 赋值节点到target
         */
        void moveToTarget(Node target) {
            int start = target.keyNumber;
            if (!leaf) {
                for (int i = 0; i <= keyNumber; i++) {
                    target.children[start + i] = children[i];
                }
            }
            for (int i = 0; i <= keyNumber; i++) {
                target.keys[start + i] = keys[i];
            }
        }
    }

    //定根节点
    Node root;
    //最小度数
    int t;
    int MIN_KEY_NUMBER;//key最小数目
    int MAX_KEY_NUMBER;//key最大数目

    public BTree() {
        this(2);
    }

    public BTree(int t) {
        this.t = t;
        root = new Node(2);
        MAX_KEY_NUMBER = t * 2 - 1;
        MIN_KEY_NUMBER = t - 1;
    }

    /**
     * 判断是否存在
     */
    public Boolean contains(int key) {
        return root.get(key) != null;
    }

    /**
     * 新增
     * 1. 当前节点寻找空位,
     * 2. 如果没找到空为
     * 如果节点是叶子节点,直接插入
     * 如果节点是非叶子节点,继续再children【】中查找
     * 3. 根据MAX_KEY_NUMBER 判单是否进行分裂
     */
    public void put(int key) {
        doPut(root, key, null, 0);
    }

    /**
     * put的递归方法
     */
    public void doPut(Node node, int key, Node parent, int index) {
        int i = 0;
        //当前节点寻找位置
        while (i < node.keyNumber) {
            //找到节点,直接替换值
            if (key == node.keys[i]) {
                //执行替换值
                //这个tree没有写value,所以直接return
                return;
            }
            //找到空位,插入
            if (node.keys[i] > key) {

                break;
            }
            i++;
        }
        //判断是否是子节点
        if (node.leaf) {
            node.insertKey(key, i);
        } else {
            doPut(node.children[i], key, node, i);
        }
        //分裂
        if (node.keyNumber >= MAX_KEY_NUMBER) {
            split(node, parent, index);
        }


    }

    /**
     * 分裂
     * 1.创建right节点(分裂后相当于left),把t以后的key和children都拷贝过去
     * 2.t-1 处的key插入到parent的index处,index作为索引
     * 3.right节点作为parent的孩子插入到index+1处
     */
    private void split(Node left, Node parent, int index) {
        //如果是根节点,则需要进行特殊处理
        if (parent == null) {
            Node newRoot = new Node(t);
            //设置属性为非根节点
            newRoot.leaf = false;
            newRoot.insertChildren(left, 0);
            this.root = newRoot;
            parent = newRoot;
        }
        //1.创建right节点
        Node right = new Node(t);
        //2.创建的right节点和分裂的节点应该是同一层级
        right.leaf = left.leaf;
        //3.将left的t往后的节点拷贝到right中
        System.arraycopy(left.keys, t, right.keys, 0, t - 1);
        //3.1 如果是非叶子节点,则需要拷贝孩子
        if (!left.leaf) {
            System.arraycopy(left.children, t, right.children, 0, t);
        }
        //4.新节点有效数目更新,老节点有效数目更新
        right.keyNumber = t - 1;
        left.keyNumber = t - 1;

        //5. 获取中间的key
        int mid = left.keys[t - 1];
        //6.中间节点插入到父节点
        parent.insertKey(mid, index);
        //7.right作为父节点的孩子
        parent.insertChildren(right, index + 1);
    }

    /**
     * 删除
     * 删除节点中某个元素
     */
    public void remove(int key) {
        doRemove(null,root,0,key);
    }

    /**
     * 执行删除的递归方法
     */
    private void doRemove(Node panret,Node node,int index, int key) {
        int i = 0;
        while (i < node.keyNumber) {
            //找到节点
            if (node.keys[i] >= key) {
                break;
            }
            i++;
        }
        //是否是叶子节点
        if (node.leaf) {
            //超出范围
            if (i >= node.keyNumber) {
                return;
            } else {
                //找到节点
                if (node.keys[i] == key) {
                    //删除指定位置的key
                    node.removeKey(i);
                } else {
                    //没找到
                    return;
                }
            }
        } else {
            //超出范围
            //找最右侧的叶子节点
            if (i >= node.keyNumber) {
                doRemove(node,node.children[i],i, key);
            } else {
                //找到节点
                if (node.keys[i] == key) {
                    //找到后继节点
                    //寻找右侧第一个节点
                    Node s = node.children[i + 1];
                    //不是叶子节点,继续向左找
                    while (!s.leaf) {
                        s = s.children[0];
                    }
                    //后继key
                    int sKey = s.keys[0];
                    //找到索引i处的key 进行替换
                    node.keys[i] = sKey;
                    //删除找到的这个后继key
                    doRemove(node,node.children[i + 1],i+1,sKey);
                    //直接删除也可以
                    //s.removeKey(0);
                } else {
                    //没找到
                    //找对应索引位置的叶子节点
                    doRemove(node,node.children[i],i, key);
                }
            }
        }
        //调整平衡
        if (node.keyNumber < MIN_KEY_NUMBER) {
            balance(panret,node,i);
        }

    }

    /**
     * 平衡调整
     *
     * @param parent 待调整节点父节点
     * @param x      待调整节点
     * @param i      待调整节点索引
     */
    private void balance(Node parent, Node x, int i) {
        //待调整节点为根节点,特殊处理
        if (x == root) {
            if(root.keyNumber == 0 && root.children[0] != null){
                root = root.children[0];
            }
            return;
        }
        Node leftSibling = parent.childLeftSibling(i);//左兄弟
        Node rightSibling = parent.childRightSibling(i);//右兄弟
        //如果左兄弟数量足够,则右旋
        if (leftSibling != null && leftSibling.keyNumber > MIN_KEY_NUMBER) {
            //1.取父节点i-1索引的key 插入到x的索引0位置中
            x.insertKey(parent.keys[i - 1], 0);
            //2.取左兄弟的最右侧key,插入到父节点i-1位置中
            parent.keys[i - 1] = leftSibling.removeRight();
            //3.不是叶子节点 处理child,
            if (!leftSibling.leaf) {
                //将最右侧的孩子,放入x的索引0位置
                x.insertChildren(leftSibling.removeRightChild(), 0);
            }
            return;
        }
        //如果右兄弟数量足够,则左旋
        if (rightSibling != null && rightSibling.keyNumber > MIN_KEY_NUMBER) {
            //1.取父节点i索引的key 插入到x的索引0位置中
            x.insertKey(parent.keys[i], x.keyNumber);
            //2.取右兄弟的最左侧key,插入到父节点i位置中
            parent.keys[i] = rightSibling.removeLeft();
            //3.不是叶子节点 处理child,
            if (!rightSibling.leaf) {
                //将最左侧的孩子,放入x的索引x.keyNumber+1位置
                x.insertChildren(rightSibling.removeLeftChild(), x.keyNumber+1);
            }
            return;
        }
        //如果左右数量都不够,则合并
        if(leftSibling !=  null){
            //左兄弟不为空,自身向左合并
            //1.将parent索引i位置的child删除
            parent.removeChild(i);
            //2.将parent i-1处的key,移动到左兄弟最右侧
            leftSibling.insertKey(parent.removeKey(i-1), leftSibling.keyNumber);
            //3.x移动到左兄弟处
            x.moveToTarget(leftSibling);
        }else{
            //右兄弟不为空,向自身合并
            //1.将parent索引i+1位置的child删除
            parent.removeChild(i+1);
            //2.将parent i处的key,移动到自身最右侧
            x.insertKey(parent.removeKey(i), x.keyNumber);
            //3.右兄弟移动到x
            rightSibling.moveToTarget(x);
        }

    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值