高阶数据结构

本文详细介绍了AVL树和红黑树这两种平衡搜索树的数据结构。AVL树是一种严格平衡的搜索树,保证任意节点的左右子树高度差不超过1。红黑树则是一种弱平衡的二叉搜索树,允许节点间存在一定的不平衡,但保证任意路径上的黑色节点数量相同。文章详细阐述了AVL树的插入操作、平衡因子调节和插入过程的正确性证明,以及红黑树的插入规则和平衡调整策略。最后,还简单提到了B-树的概念和插入规则。

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

一、AVL树

1.AVL的定义和特征

特征:搜索树&&平衡树
搜索树:任取结点,其左孩子.key<其.key<右孩子.key,因此中序遍历是有序的,按照key值顺序大小排列
平衡树:结点左子树的高度和结点右子树的高度的高度差的绝对值不超过1
平衡因子:bf(node)=H(node.left)-H(node.right)

2.AVL的操作步骤

①查找

在这里插入图片描述
如果 key==current.key: 查找到了key,current就是包含了key的结点
如果 key<current.key: current=current.left
如果 key>current.key: current=current.right
如果current == null :key不在集合中

时间复杂度~O(树的高度) 即O(log(n))

②插入
1.按照普通搜索树的插入规则进行结点的插入
2.随着插入的完成,导致平衡因子出现的变化,所以要进行平衡因子的调节

BF(node)==0
BF(parent.parent) 不变
BF(parent)

  • if(node是parent.left) 则 H(parent.left)++ 即 BF(parent)++

  • else(node是parent.right) 则 H(parent.right)++ 即 BF(parent)–

失衡情况:
在这里插入图片描述

LL:对parent右旋,插入结束
LR:对node左旋,对parent右旋,插入结束
RR:对parent左旋,插入结束
RR:对node右旋,对parent左旋,插入结束

3.AVL树的插入过程正确性的证明

请添加图片描述

4.AVL树的实现

结点

public class AVLNode {
    public long key;
    public AVLNode left;
    public AVLNode right;
    //特殊字段
    public int bf;    //平衡因子
    public AVLNode parent;  //父结点

    @Override
    public String toString() {
        return "AVLNode{" +
                "key=" + key +
                ", bf=" + bf +
                '}';
    }
}

AVL树

public class AVLTree {
    public AVLNode root = null;
    public int size = 0;    // 保存树的结点个数

    public void insert(long key) {
        AVLNode node = new AVLNode();
        node.key = key;
        node.left = null;
        node.right = null;
        node.parent = null;
        node.bf = 0;

        if (root == null) {
            root = node;
            size++;
            return;
        }

        AVLNode current = root;
        AVLNode parent = null;
        while (current != null) {
            if (key == current.key) {
                return;
                //throw new RuntimeException("插入失败,key 有重复: " + key);
            } else if (key < current.key) {
                parent = current;
                current = current.left;
            } else {
                parent = current;
                current = current.right;
            }
        }

        node.parent = parent;
        if (key < parent.key) {
            parent.left = node;
        } else {
            parent.right = node;
        }

        avlAdjust(parent, node);

        size++;
    }

    private void avlAdjust(AVLNode parent, AVLNode node) {
        // parent != null && node != null

        while (true) {
            // 进行平衡因子的调整
            if (node == parent.left) {
                parent.bf++;
            } else {
                parent.bf--;
            }

            // 第一种情况
            if (parent.bf == 0) {
                return;
            }

            // 第二种情况
            if (parent.bf == -1 || parent.bf == 1) {
                node = parent;
                parent = parent.parent;

                if (parent == null) {
                    // 向上回溯到根的位置了
                    return;
                }
                continue;
            }

            // 情况三
            // parent.bf == -2 || parent.bf == 2
            break;
        }

        // 一定是出现失衡情况了
        if (parent.bf == 2) {
            if (node.bf == 1) {
                // LL 失衡
                rightRotate(parent);

                parent.bf = node.bf = 0;
            } else {
                // LR 失衡
                // node.bf == -1
                AVLNode c = node.right;
                int condition;
                if (parent.right == null) {
                    condition = 1;
                } else if (c.bf == 1) {
                    condition = 2;
                } else {
                    condition = 3;
                }

                leftRotate(node);
                rightRotate(parent);

                if (condition == 1) {
                    parent.bf = node.bf = c.bf = 0;
                } else if (condition == 2) {
                    parent.bf = -1;
                    node.bf = c.bf = 0;
                } else {
                    parent.bf = c.bf = 0;
                    node.bf = 1;
                }
            }
        } else {
            // parent.bf == -2
            if (node.bf == -1) {
                // RR 失衡
                leftRotate(parent);

                parent.bf = node.bf = 0;
            } else {
                // RL 失衡
                // node.bf == 1

                AVLNode c = node.left;
                int condition;
                if (parent.left == null) {
                    condition = 1;
                } else if (c.bf == 1) {
                    condition = 2;
                } else {
                    condition = 3;
                }

                rightRotate(node);
                leftRotate(parent);

                if (condition == 1) {
                    parent.bf = node.bf = 0;
                } else if (condition == 2) {
                    parent.bf = c.bf = 0;
                    node.bf = -1;
                } else {
                    parent.bf = 1;
                    node.bf = c.bf = 0;
                }
            }
        }
    }

    // 以 m 为结点,进行旋转
    private void leftRotate(AVLNode m) {
        // m 代表图中的 b 结点
        // parent 代表 b 结点可能存在的父亲
        AVLNode parent = m.parent;
        // right 代表图中的 a 结点
        AVLNode right = m.right;
        // leftOfRight 代表图中的可能存在的乙子树的根结点
        AVLNode leftOfRight = right.left;
        /*
        其中: m != null && right != null
        但是: parent 不保证 !null, leftOfRight 不保证 !null
         */

        right.parent = parent;  // 蓝色线的关系
        // 黑色线的关系
        if (parent == null) {
            // m 是 root
            root = right;
        } else {
            if (m == parent.left) {
                parent.left = right;
            } else {
                parent.right = right;
            }
        }

        right.left = m; // 黑色线的关系
        m.parent = right;   // 蓝色线的关系

        m.right = leftOfRight;
        if (leftOfRight != null) {
            leftOfRight.parent = m;
        }
    }

    private void rightRotate(AVLNode m) {
        AVLNode parent = m.parent;
        AVLNode left = m.left;
        AVLNode rightOfLeft = left.right;

        left.parent = parent;
        if (parent == null) {
            root = left;
        } else {
            if (m == parent.left) {
                parent.left = left;
            } else {
                parent.right = left;
            }
        }

        left.right = m;
        m.parent = left;

        m.left = rightOfLeft;
        if (rightOfLeft != null) {
            rightOfLeft.parent = m;
        }
    }

    public static void main(String[] args) {
        AVLTree tree = new AVLTree();

        AVLNode n5 = new AVLNode();
        n5.key = 5;

        AVLNode n6 = new AVLNode();
        n6.key = 6;

        AVLNode n7 = new AVLNode();
        n7.key = 7;

        n5.right = n6;
        n6.parent = n5;

        n6.right = n7;
        n7.parent = n6;

        tree.root = n5;

        tree.leftRotate(tree.root);
        System.out.println("Hello");
    }
}

二、红黑树

1.红黑树的定义

红黑树是一种“平衡”二叉搜索树
“平衡”:相比较于AVL树来说,是一种弱平衡
在红黑树中,任意从根到叶子的路径中,LEN(最长的路径)<=2*LEN(最短的路径)
详细规则:

  • 红黑树中的结点:或红或黑
  • 红黑树的根一定是黑色的
  • 红黑树的“叶子结点”一定是黑色的
    注:平时我们理解的叶子:node.left == null && node.right == null,而此处的叶子:node==null
  • 红黑树中,任意路径(从根到叶子),红色不能和红色相邻
  • 红黑树中,任意路径(从根到叶子),黑色结点的数量必须一样多

查找操作:同普通的搜索树规则一样
插入操作:

  • 按照普通搜索树的方式插入,新插入的结点一定是红色
  • 判断是否破坏了红色和红色不能相邻的规则,如果破坏了需进行平衡调整操作
  • 将根置为黑色

2.平衡调整规则

① uncle存在 && uncle的颜色是红色

  • parent的颜色=黑色
  • uncle的颜色=黑色
  • grandpa的颜色=红色
  • 把grandpa视为node,一直回溯到根结点(回溯退出的情况:回溯到根上、回溯到没破坏红色不能相邻)
    ②如果关系不一致
  • 旋转祖父
  • 祖父的颜色改成红色
  • 父亲的颜色改成黑色

2.实现

结点

public class RBTreeNode {
    public static final boolean BLACK=true;
    public static final boolean RED=false;

    public long key;
    public RBTreeNode left;
    public RBTreeNode right;

    public RBTreeNode parent;

    public boolean color;

    public RBTreeNode(long key, RBTreeNode parent, boolean color) {
        this.key = key;
        this.parent = parent;
        this.color = color;
    }

    @Override
    public String toString() {
        return "RBTreeNode{" +
                "key=" + key +
                ", color=" + color +
                '}';
    }
}

红黑树


/**
 * 红黑树
 */
public class RBTree {
    /**
     * 保存红黑树的根结点
     */
    public RBTreeNode root = null;
    /**
     * 保存红黑树中的结点个数
     */
    public int size = 0;

    /**
     * 向红黑树中插入新的关键字
     * @param key 关键字
     * @return 是否插入成功
     * true: 插入成功
     * false: 插入失败(key 出现重复)
     */
    public boolean insert(long key) {
        // 1. 按照普通搜索树的方式进行插入
        if (root == null) {
            root = new RBTreeNode(key, null, BLACK);
            size++;
            return true;
        }

        RBTreeNode parent = null;
        RBTreeNode current = root;

        while (current != null) {
            if (key == current.key) {
                return false;
            } else if (key < current.key) {
                parent = current;
                current = current.left;
            } else {
                parent = current;
                current = current.right;
            }
        }

        /**
         * 根据插入规则,每次新插入的结点,一定是红色的
         */
        RBTreeNode node = new RBTreeNode(key, parent, RED);
        if (key < parent.key) {
            parent.left = node;
        } else {
            parent.right = node;
        }
        size++;

        /**
         * 进行红黑树规则的判断 + 平衡调整过程
         */
        adjustBalance(node);

        return true;
    }

    private void adjustBalance(RBTreeNode node) {
        while (true) {
            RBTreeNode parent = node.parent;
            if (parent == null) {
                break;
            }

            if (parent.color == BLACK) {
                break;
            }

            /**
             * 一定破坏了"红色不能相邻"的规则
             */
            RBTreeNode grandpa = parent.parent;

            // 找到叔叔结点
            if (parent == grandpa.left) {
                RBTreeNode uncle = grandpa.right;
                if (uncle != null && uncle.color == RED) {
                    /**
                     * 情况1:叔叔存在 并且 叔叔的颜色是红色
                     * 步骤:
                     * 1. 叔叔和父亲的颜色改成黑色
                     * 2. 祖父的颜色改成红色
                     * 3. 把祖父视为 node,再去判断是否违反规则了
                     */
                    parent.color = uncle.color = BLACK;
                    grandpa.color = RED;
                    node = grandpa;
                    continue;
                } else {
                    // 判断 grandpa <-> parent 和 parent <-> node 的关系是否不一致
                    // 已知 parent 是 grandpa 的左边
                    if (node == parent.right) {
                        leftRotate(parent);

                        //swap(parent, node);
                        parent = node;
                    }

                    // 接下来统一处理关系一致的情况
                    rightRotate(grandpa);
                    grandpa.color = RED;
                    parent.color = BLACK;
                    break;
                }
            } else {
                RBTreeNode uncle = grandpa.left;
                if (uncle != null && uncle.color == RED) {
                    /**
                     * 情况1:叔叔存在 并且 叔叔的颜色是红色
                     * 步骤:
                     * 1. 叔叔和父亲的颜色改成黑色
                     * 2. 祖父的颜色改成红色
                     * 3. 把祖父视为 node,再去判断是否违反规则了
                     */
                    parent.color = uncle.color = BLACK;
                    grandpa.color = RED;
                    node = grandpa;
                    continue;
                } else {
                    // 判断 grandpa <-> parent 和 parent <-> node 的关系是否不一致
                    // 已知 parent 是 grandpa 的右边
                    if (node == parent.left) {
                        rightRotate(parent);

                        //swap(parent, node);
                        parent = node;
                    }

                    // 接下来统一处理关系一致的情况
                    leftRotate(grandpa);
                    grandpa.color = RED;
                    parent.color = BLACK;
                    break;
                }
            }
        }

        /**
         * 无论之前是什么情况,统一把根改成黑色
         * 走到此处时,root 一定不是 null
         */
        root.color = BLACK;
    }


    // 以 m 为结点,进行旋转
    private void leftRotate(RBTreeNode m) {
        // m 代表图中的 b 结点
        // parent 代表 b 结点可能存在的父亲
        RBTreeNode parent = m.parent;
        // right 代表图中的 a 结点
        RBTreeNode right = m.right;
        // leftOfRight 代表图中的可能存在的乙子树的根结点
        RBTreeNode leftOfRight = right.left;
        /*
        其中: m != null && right != null
        但是: parent 不保证 !null, leftOfRight 不保证 !null
         */

        right.parent = parent;  // 蓝色线的关系
        // 黑色线的关系
        if (parent == null) {
            // m 是 root
            root = right;
        } else {
            if (m == parent.left) {
                parent.left = right;
            } else {
                parent.right = right;
            }
        }

        right.left = m; // 黑色线的关系
        m.parent = right;   // 蓝色线的关系

        m.right = leftOfRight;
        if (leftOfRight != null) {
            leftOfRight.parent = m;
        }
    }

    private void rightRotate(RBTreeNode m) {
        RBTreeNode parent = m.parent;
        RBTreeNode left = m.left;
        RBTreeNode rightOfLeft = left.right;

        left.parent = parent;
        if (parent == null) {
            root = left;
        } else {
            if (m == parent.left) {
                parent.left = left;
            } else {
                parent.right = left;
            }
        }

        left.right = m;
        m.parent = left;

        m.left = rightOfLeft;
        if (rightOfLeft != null) {
            rightOfLeft.parent = m;
        }
    }
}

三、B-树系列

1.定义

B-树是一棵多叉、平衡搜索树
B-树中每一个结点在实现时人为规定一个key的上限
B-树结点中的key时按照key的顺序进行保存
我们把key的个数记为size,则child的个数一定是size+1

2.插入规则

  1. 所有插入过程,只能发生在叶子上,不能发生在非叶子结点上
  2. 插入时,每个结点的key是有上限的
    完成插入后:未达到上限则插入结束,否则进行结点分裂过程
  3. 关于结点分裂过程
  • 找到key的中间值,把下标记为index,不一定是新插入的结点,偶数的话,前一个或者后一个都可以
  • 把当前结点分成两个结点,【0,index)key留在当前结点当中,【index】提到父结点当中,(index,size)搬到新结点中

3.代码实现

BTreeNode


public class BTreeNode {
    /**
     * 先规定,一个 B-树结点中,能存的 key 的数量的上限,要求 size(key) <= KEY_LIMIT
     * 在实际的应用中,一般 KEY_LIMIT 都是 1000 数量级以上的
     */
    public static final int KEY_LIMIT = 4;


    /**
     * 去保存结点中所有的 key
     * 使用数组(线性结构)进行维护,需要维护 key 与 key 之间的有序性
     * 从空间利用率角度考虑,new long[KEY_LIMIT] 就足够了
     * 但我们多申请一个空间,方便进行结点分裂代码的编写(用于保存即将分裂状态下的所有 key)
     */
    public long[] keyList = new long[KEY_LIMIT + 1];

    /**
     * 记录目前拥有的 key 的个数
     */
    public int size = 0;

    /**
     * 去维护结点中所有的 child
     * 同理,多申请一个空间
     * 本来,child 就比 key 多一个
     * new BTreeNode[KEY_LIMIT + 2]
     */
    public BTreeNode[] childList = new BTreeNode[KEY_LIMIT + 2];
    /**
     * 我们不维护 child 的个数,因为 其个数一定是 size + 1
     */

    /**
     * 保存该结点的父结点,null 代表该结点是根结点
     */
    public BTreeNode parent = null;

    @Override
    public String toString() {
        return String.format("%d: %s", size, Arrays.toString(Arrays.copyOf(keyList, size)));
    }

    /**
     * 查找 key 位于当前结点的哪个子树中
     * @param key 关键字
     * @return
     * this: key 位于当前结点
     * 其他: 位于孩子结点中,可能是 null
     */
    public BTreeNode findKey(long key) {
        if (key > keyList[size - 1]) {
            // child 比 key 多一个
            // keyList 的最后一个元素位于 [size - 1]
            // childList 的最后一个元素位于 [size]
            return childList[size];
        }

        for (int i = 0; i < size; i++) {
            if (key == keyList[i]) {
                // key 就在当前结点中
                return this;
            } else if (key < keyList[i]) {
                // 由于我们是从左向右遍历的
                // 所以 keyList[i] 中的元素是 "第一个" 大于 key 的关键字元素
                return childList[i];
            } else {
                // key > keyList[i] 的情况下,我们什么都不需要做,继续遍历查找
                // 第一个 大于 key 的元素
            }
        }

        // 我们的代码组织方式使得,理论上,不应该走到这个位置
        // 但 java 编译器分析不出来,所以,为了让编译器不报错,我们随便返回一个值
        // 这个 return 没有任何执行的意义
        return null;
    }

    /**
     * 用来作为插入的返回值类型的类
     */
    public static class InsertResult {
        public long key;        // 分裂出的 key
        public BTreeNode node;  // 分裂出的新结点
    }

    /**
     * 将指定关键字 key,插入叶子结点中
     * @param key 关键字
     * @return
     * null: 插入之后不需要分裂
     * !null: 发生了分裂
     */
    public InsertResult insertKey(long key) {
        // 1. 如果 key 按序插入到 keyList 中
        insertIntoKeyList(key);
        size++;
        // 2. 判断是否需要进行分裂
        // 3. 如果有必要,进行结点分裂
        if (shouldSplit()) {
            return splitLeaf();
        }

        return null;
    }

    /**
     * 将指定关键字 key 和 chid,插入非叶子结点中
     * @param key 关键字
     * @param child > key 的孩子
     * @return
     */
    public InsertResult insertKeyWithChild(long key, BTreeNode child) {
        // 1. 如果 key 按序插入到 keyList 中
        int index = insertIntoKeyList(key);
        // 2. 根据 index 进行 child 的插入,暂时没有进行 size 的变动
        // child[0, size]
        // [0, index] 不动
        // [index + 1] 要插入 child 的下标
        // [index + 1, size] 搬到 [index + 2, size + 1]  元素个数 size - (index + 1) + 1 == size - index
        System.arraycopy(childList, index + 1, childList, index + 2, size - index);
        childList[index + 1] = child;
        // 这个时候再让 size++
        size++;

        // 2. 判断是否需要进行分裂
        // 3. 如果有必要,进行结点分裂
        if (shouldSplit()) {
            return splitNotLeaf();
        }

        return null;
    }

    public InsertResult splitNotLeaf() {
        // 因为一会儿 this.size 在中途会变化,先提前记录
        int size = this.size;

        InsertResult result = new InsertResult();
        BTreeNode node = new BTreeNode();

        // 1. 找到 keyList 的中间位置
        int index = size / 2;

        // 2. 保存需要分裂出的 key
        result.key = keyList[index];

        /**
         * 3. 处理 key 的分裂
         * 哪些 key 应该留在当前结点中      [0, index)    一共有 index 个
         * 哪个 key 是分裂出的 key         [index]
         * 哪些 key 应该在新结点中         (index, size)  一共有 size - index - 1
         */
        // 先把 (index, size) 位置的 key 搬到新结点中
        System.arraycopy(keyList, index + 1, node.keyList, 0, size - index - 1);
        node.size = size - index - 1;

        // 把 [index, size) 所有 key 重置为默认值 (0),重置 size
        Arrays.fill(keyList, index, size, 0);
        this.size = index;

        /**
         * 4. 我们对非叶子结点进行分裂,非叶子结点有孩子,所以也进行分裂
         */
        System.arraycopy(this.childList, index + 1, node.childList, 0, size - index);
        Arrays.fill(this.childList, index + 1, size + 1, null);
        for (int i = 0; i < size - index; i++) {
            node.childList[i].parent = node;
        }

        /**
         * 5. 处理分裂的 parent 的问题
         * 1) this.parent 不变,分裂不影响父子关系
         * 2) node.parent 和 this 是同一个父亲
         */
        node.parent = this.parent;

        result.node = node;

        return result;
    }

    public InsertResult splitLeaf() {
        // 因为一会儿 this.size 在中途会变化,先提前记录
        int size = this.size;

        InsertResult result = new InsertResult();
        BTreeNode node = new BTreeNode();

        // 1. 找到 keyList 的中间位置
        int index = size / 2;

        // 2. 保存需要分裂出的 key
        result.key = keyList[index];

        /**
         * 3. 处理 key 的分裂
         * 哪些 key 应该留在当前结点中      [0, index)    一共有 index 个
         * 哪个 key 是分裂出的 key         [index]
         * 哪些 key 应该在新结点中         (index, size)  一共有 size - index - 1
         */
        // 先把 (index, size) 位置的 key 搬到新结点中
        System.arraycopy(keyList, index + 1, node.keyList, 0, size - index - 1);
        node.size = size - index - 1;

        // 把 [index, size) 所有 key 重置为默认值 (0),重置 size
        Arrays.fill(keyList, index, size, 0);
        this.size = index;

        /**
         * 4. 我们对叶子结点进行分裂,叶子结点没有孩子 childList[...] = null,不需要分裂
         */

        /**
         * 5. 处理分裂的 parent 的问题
         * 1) this.parent 不变,分裂不影响父子关系
         * 2) node.parent 和 this 是同一个父亲
         */
        node.parent = this.parent;

        result.node = node;

        return result;
    }

    public boolean shouldSplit() {
        return size > KEY_LIMIT;
    }

    public int insertIntoKeyList(long key) {
        // keyList 中有效的下标范围是 [0, size)
        // 从后向前遍历 [size - 1, 0]
        int i;
        for (i = size - 1; i >= 0; i--) {
            if (keyList[i] < key) {
                // 第一次碰到了 keyList[i] < key
                break;
            }

            // 没有 keyList[i] == key
            // 继续遍历查找,同时将数据向后搬一格
            keyList[i + 1] = keyList[i];
        }

        // 1. i == -1
        // 2. keyList[i] < key
        // 不管怎么样,key 都应该放在 [i + 1]
        keyList[i + 1] = key;

        return i + 1;
    }

    public static void testFindKey() {
        BTreeNode node = new BTreeNode();
        node.keyList[0] = 10;
        node.keyList[1] = 20;
        node.keyList[2] = 30;
        node.keyList[3] = 40;
        node.size = 4;

        BTreeNode= new BTreeNode();
        BTreeNode= new BTreeNode();
        BTreeNode= new BTreeNode();
        BTreeNode= new BTreeNode();
        BTreeNode= new BTreeNode();
        node.childList[0] =;
        node.childList[1] =;
        node.childList[2] =;
        node.childList[3] =;
        node.childList[4] =;

        /**
         * 测试用例:
         * 执行动作           期望结果
         * < 10              期望   甲
         * == 10             期望   node
         * == 20             期望   node
         * (10, 20)          期望   乙
         * (20, 30)          期望   丙
         * (30, 40)          期望   丁
         * > 40              期望   戊
         * == 40             期望   node
         */
        // 引用1 == 引用2: 判断两个引用是否指向同一个对象
        System.out.println(node.findKey(3) ==);
        System.out.println(node.findKey(10) == node);
        System.out.println(node.findKey(20) == node);
        System.out.println(node.findKey(13) ==);
        System.out.println(node.findKey(29) ==);
        System.out.println(node.findKey(31) ==);
        System.out.println(node.findKey(300) ==);
        System.out.println(node.findKey(40) == node);
    }

    public static void testInsertKey() {
        BTreeNode parent = new BTreeNode();

        BTreeNode node = new BTreeNode();
        node.parent = parent;

        // 第一个测试用例
        // 理论上这个不应该测,因为 B-树的插入过程中从不会有往一个空的(不是 null)结点中插入的
        // B-树的结点都是分裂出来的,不是手动 new 出来的
        // B-树一定包含从原来的结点中分出来的 key
        InsertResult r = node.insertKey(15);
        System.out.println(r == null);
        System.out.println(node.size == 1);
        System.out.println(node.keyList[0] == 15);
        System.out.println(node.parent == parent);

        // 测试用例 2
        r = node.insertKey(7);
        System.out.println(r == null);
        System.out.println(node.size == 2);
        System.out.println(node.keyList[0] == 7);
        System.out.println(node.keyList[1] == 15);
        System.out.println(node.parent == parent);

        // 测试用例 3
        r = node.insertKey(19);
        System.out.println(r == null);
        System.out.println(node.size == 3);
        System.out.println(node.keyList[0] == 7);
        System.out.println(node.keyList[1] == 15);
        System.out.println(node.keyList[2] == 19);
        System.out.println(node.parent == parent);

        // 测试用例 4
        r = node.insertKey(10);
        System.out.println(r == null);
        System.out.println(node.size == 4);
        System.out.println(node.keyList[0] == 7);
        System.out.println(node.keyList[1] == 10);
        System.out.println(node.keyList[2] == 15);
        System.out.println(node.keyList[3] == 19);
        System.out.println(node.parent == parent);

        // 测试用例 5
        r = node.insertKey(17);
        System.out.println(r != null);
        System.out.println(node.size == 2);
        System.out.println(node.keyList[0] == 7);
        System.out.println(node.keyList[1] == 10);
        System.out.println(node.keyList[2] == 0);
        System.out.println(node.keyList[3] == 0);
        System.out.println(node.keyList[4] == 0);
        System.out.println(node.parent == parent);
        System.out.println(r.key == 15);
        System.out.println(r.node.size == 2);
        System.out.println(r.node.keyList[0] == 17);
        System.out.println(r.node.keyList[1] == 19);
        System.out.println(r.node.keyList[2] == 0);
        System.out.println(r.node.keyList[3] == 0);
        System.out.println(r.node.keyList[4] == 0);
        System.out.println(r.node.parent == parent);
    }

    public static void main(String[] args) {
        //testFindKey();
        testInsertKey();
    }
}

BTree


/**
 * B-树定义
 */
public class BTree {
    /**
     * B-树的根结点
     */
    public BTreeNode root = null;

    /**
     * B-树中的 key 的个数
     * 备注:和二叉搜索树不同,B-树的 key 的个数 不等于 结点的个数
     */
    public int size = 0;

    public boolean insert(long key) {
        if (insertWithoutSize(key)) {
            size++;
            return true;
        }

        return false;
    }

    public boolean insertWithoutSize(long key) {
        // 1. 是否是空树
        if (root == null) {
            // root = 构建一个包含 key 的结点;
            root = new BTreeNode();
            root.keyList[0] = key;
            root.size = 1;
            return true;
        }

        // 2. 不是空树的情况
        BTreeNode current = root;
        while (true) {
            BTreeNode node = current.findKey(key);

            if (node == current) {
                // key 就在 current 结点中
                // 不运行重复 key 的插入,插入失败
                return false;
            } else if (node == null) {
                // current 就是叶子结点
                // current 就是接下来要插入 key 的结点
                break;
            } else {
                // 找到了一个孩子 并且 current 不是叶子
                // 规则:插入必须发生在叶子结点上
                current = node;
            }
        }

        // 进行 key 的插入
        // current 就是我们要进行 key 插入的结点
        // current 此时一定是叶子
        InsertResult r = current.insertKey(key);

        while (true) {
            if (r == null) {
                // 插入过程中,没有发生结点分裂,直接退出
                return true;
            }

            // 说明发生分裂了
            // 需要把新分裂出的 key 和 分裂出的结点,插入到 current.parent 中
            BTreeNode parent = current.parent;
            if (parent == null) {
                // 说明 current 是根结点
                // 根结点发生分裂了
                // 需要一个新的根结点,来保存分裂出的 key
                root = new BTreeNode();
                root.keyList[0] = r.key;
                root.size = 1;
                root.childList[0] = current;
                root.childList[1] = r.node;

                // 由于原来 current 是根结点,所以其 parent == null
                // 自然分裂出的结点,跟着也是 null
                // 所以为他们设置新的父结点
                current.parent = r.node.parent = root;
                return true;
            }

            // 不是根结点分裂了
            r = parent.insertKeyWithChild(r.key, r.node);
            current = parent;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值