B,B+树

  B+树被用作数据索引,帮助数据库高效获取数据的一种数据结构。

多路查找树

  又称m-way查找树是是一种树形的存储结构,主要特点如下,

  • 每个节点存储的key数量小于m个
  • 每个节点的度小于等于m
  • 节点key按顺序排序
  • 子树key值要完全小于、大于或介于父节点之间

 以下是3路查找树的结构图

和二叉搜索树的功能相似

Btree查找树

   Btree可以看作是一种平衡的多路查找树,他是针对磁盘而设计的数据结构(具体可参考算法导论),减少读出/写入页次数。

主要特点如下,

1)每个节点x有三个属性:

  a、x.n—>关键字个数

  b、关键字递增排序

  c、x.leaf—>节点是否属于叶子节点

2)每个节点有x.n+1个孩子节点,叶节点没有子女。

3) 其左孩子节点<每个节点关键字 < 其右孩子节点

4)每个叶子节点具有相同的深度,即树的高度h。

5)每个节点用最小度数 t 来表示其关键字个数的上下界,或者孩子节点(分支因子)的个数的上下界。一般,每个非根节点中所包含的关键字个数 j 满足:

t-1 <= j <= 2*t - 1

根节点至少包括一个关键字,若非叶子节点,则至少两个分子,即 t>= 2。

Btree的插入

        B树的插入是将关键字插入到已存在的节点,而节点可能已经是满节点,就会破坏B树的性质。因此不能将关键字插入到满节点上。根据B树的规则,每个节点的关键字个数在[M-1, 2M-1]之间,故当keyword(要插入的关键字)要加入到某个叶子时,如果该叶子节点已经有2M-1个关键字,则再加入keyword就违反了B树的定义,这时就需要对该叶子节点进行分裂,将叶子以中间节点为界,分成两个包含M-1个关键字的子节点,同时把中间节点提升到该叶子的父节点中,如果这样使得父节点的关键字个数超过2M-1,则要继续向上分裂,直到根节点,根节点的分裂会使得树加高一层。

        为了解决上面问题,我们需要不断地回溯(插入最坏情况,会增加磁盘读取次数),这显然比较复杂,我们可以未雨绸缪:我们不是等到发现是否真的需要分裂一个满节点时才做插入操作。相反地,当沿着树向下查找要插入关键字所处位置时,就分裂沿途遇到的每个满节点。这样做后,每当要分裂一个满节点时,就能保证其双亲不是满节点。(先将结节分裂,然后再插入,很多朋友的博克都是先插入再分裂
 

假设初始的B树如下:

经过一系列的插入操作后:

这棵B树T为3.所以  2<=节点个数<=5

图(b)  B被插入,这是一个对叶结点的简单插入

图(c)  Q被插入,叶结点[RSTUV]分裂成 【RS】【UV】

图(d)  L被插入, 我们看到 ROOT 被拆分了,L明明还能放入 【J,K】的框中,问题是当程序执行到ROOT节点的时候,程序无法判断下面节点的容量,所以先将ROOT拆分(防止最坏的情况)。

图(e)  F被插入,叶结点[ABCDE]分裂成 【AB】【DE】,C向上插入成【CGM】,现插入F到【DEF】

Btree的删除

B树的删除比插入操作更加复杂,插入操作只需考虑三种情况,而删除操作需要考虑的情况很多,情况如下:

经过上面的插入操作后,紧接在进行一系列删除操作:

图(b)  F被删除,这是一个对叶结点的简单删除(情况1)

图(c)  M被删除,叶结点[JKL] 中 L为M前驱被提升为M位置(情况2.a),前驱节点如果少,则后继补(情况2.b)

图(d)  G被删除, 拿G的前驱和后继去替换G的位置,都将导至 前驱/后继节点数<2,下降G合并结点,然后现删除G得【DEJK】(情况2.c)

 

图(e)  D被删除,ROOT发现【C,L】已经只有2个关键字,最坏的情况(子树节点关键字都是2)出现 应该以P为节点左旋,但【T,X】也只有2个,  【P】下沉和【TX】合为【CLPTX】(情况3,b)

图(f)  B被删除,以E为节点做左旋(情况3.a)

Btree的代码

public class BTree<K extends Comparable<K>> {

    private final int t;

    private BTreeNode<K> root;

    public BTree(int t) {
        root = new BTreeNode<K>(t);
        this.t = t;
    }

    // 插入root节点,发现要满了,先分裂
    public void insert(K key) {
        if (root.isFull()) {
            BTreeNode.Node<K> node = root.spilt();
            root = new BTreeInternalNode<K>(t);
            root.insert(node, 0);
        }
        root.insertNotFull(key);
    }

    // 插入root节点数为1且左右子树less的时候,他remove有可能产生空层,这里先把空层去掉
    public void remove(K key) {
        if (this.root.n == 1) {
            BTreeNode temp = this.root;
            BTreeNode.Node<K> node = temp.getMergeNode(key);
            if (node.isLess()) {
                this.root = temp.merge(node);
            }
        }
        this.root.remove(key);
    }

    @Override
    public String toString() {
        return this.root.toString();
    }

    public static void main(String[] args) {
        BTree<Integer> btree = new BTree<Integer>(2);
        // btree.insert(50);
        btree.insert(30);
        // btree.insert(70);
        // btree.insert(27);
        // btree.insert(47);
        // btree.insert(51);
        // btree.insert(31);
        // btree.insert(71);
        // btree.insert(21);
        // btree.insert(41);
        // btree.insert(15);
        // btree.insert(11);
        btree.insert(12);
        // btree.insert(17);
        btree.insert(13);
        btree.insert(52);
        btree.insert(18);
        btree.insert(19);
        System.out.println(btree);
        System.out.println("t1------------------------------------");
        btree.remove(19);
        btree.remove(18);
        btree.remove(52);
        System.out.println(btree);
        System.out.println("t2------------------------------------");
        btree.remove(30);
        btree.remove(12);
        btree.remove(13);
        System.out.println(btree);
    }
}
public class BTreeInternalNode<K extends Comparable<K>> extends BTreeNode<K> {

    private final BTreeNode[] children;

    protected BTreeInternalNode(int t) {
        super(t);
        this.children = new BTreeNode[2 * t];
    }

    /**
     * 插入一个节点,发现将插入的关系其节点要满时,则分裂节点
     */
    @Override
    protected void insertNotFull(K key) {
        Assert.checkTrue(!isFull(), "[" + this + "] is full");
        Assert.checkNotNull(key, "key");
        BTreeNode child = getSplitChild(key);
        if (child.isFull()) {
            Node<K> newNode = child.spilt();
            insert(newNode);
            child = getSplitChild(key);
        }
        child.insertNotFull(key);
    }

    @Override
    protected Node<K> spilt() {
        BTreeInternalNode<K> lf = new BTreeInternalNode<K>(t);
        BTreeInternalNode<K> rt = new BTreeInternalNode<K>(t);
        return spilt(lf, rt);
    }

    @Override
    protected void removeNotLess(K key, Node<K> node) {
        Assert.checkTrue(!node.isLess(), "node  is  less");
        int value = key.compareTo(node.getKey());
        if (value == 0) {// 删除内节点元素,对应情况2
            K newKey = null;
            if (!node.getLeft().isLess()) {
                // 前驱节点至少有t个关系字,找出K'替换,并删除 对就情况2.a
                newKey = node.getLeft().removeMaxKey();
            } else {
                // 后继节点至少有t个关系字,找出K'替换,并删除 对就情况2.b
                newKey = node.getRight().removeMinKey();
            }
            replace(node.getIndex(), newKey);
            return;
        } else if (value < 0) {
            // 要删除的key在左边,而左边刚好less,则做左旋 (情况3.a)
            if (node.getLeft().isLess()) {
                Node<K> node1 = node.getRight().removeMinNode();
                Node newNode = new Node(-1, node.getKey(), node.getLeft().getNode(n - 1).getRight(), node1.getLeft());
                node.getLeft().append(newNode);
                replace(node.getIndex(), node1.getKey());
            }
            node.getLeft().remove(key);
        } else {
            // 要删除的key在右边,而右边刚好less,则做左旋 (情况3.a)
            if (node.getRight().isLess()) {
                Node<K> node1 = node.getLeft().removeMaxNode();
                Node newNode = new Node(-1, node.getKey(), node1.getRight(), node.getRight().getNode(0).getLeft());
                node.getRight().insert(newNode, 0);
                replace(node.getIndex(), node1.getKey());
            }
            node.getRight().remove(key);
        }
    }

    /**
     *前驱节点至少有t个关系字,找出K'替换,并删除 对就情况2.a
     */
    @Override
    protected K removeMaxKey() {
        Node<K> node = getNode(n - 1);
        if (node.isLess()) {
            BTreeNode<K> merge = merge(node);
            remove(merge, n - 1);
            node = getNode(n - 1);
        }
        if (node.getRight().isLeaf()) {
            K key = node.getRight().getKey(node.getRight().n - 1);
            removeNotLess(key, node);
            return key;
        }
        return node.getRight().removeMaxKey();
    }

    /**
     *后继节点至少有t个关系字,找出K'替换,并删除 对就情况2.b
     */
    @Override
    protected K removeMinKey() {
        Node<K> node = getNode(0);
        if (node.isLess()) {
            BTreeNode<K> merge = merge(node);
            remove(merge, 0);
            node = getNode(0);
        }
        if (node.getLeft().isLeaf()) {
            K key = node.getLeft().getKey(0);
            removeNotLess(key, node);
            return key;
        }
        return node.getLeft().removeMinKey();
    }

    @Override
    protected boolean isLeaf() {
        return false;
    }

    @Override
    protected void insert(Node<K> node, int index) {
        super.insert(node, index);
        this.children[index] = node.getLeft();
        this.children[index + 1] = node.getRight();
    }

    @Override
    protected void remove(BTreeNode<K> rt, int index) {
        super.remove(rt, index);
        children[index] = rt;
    }

    @Override
    protected void rightMove(int index) {
        super.rightMove(index);
        if (this.children[index + 2] == null)
            this.children[index + 2] = children[index + 1];
        this.children[index + 1] = children[index];
        children[index] = null;
    }

    @Override
    protected void leftMove(int index) {
        super.leftMove(index);
        children[index] = children[index + 1];
        children[index + 1] = null;
    }

    @Override
    protected BTreeNode<K> getLeftChild(int index) {
        return children[index];
    }

    @Override
    protected BTreeNode<K> getRightChild(int index) {
        return children[index + 1];
    }

    @Override
    protected String toString(int lev) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            Node<K> node = getNode(i);
            if (i == 0)
                sb.append(node.getLeft().toString(lev + 1));
            sb.append(getLevSpace(lev)).append(node.getKey()).append("\n");
            sb.append(node.getRight().toString(lev + 1));

        }
        return sb.toString();
    }
}
public class BTreeNode<K extends Comparable<K>> {

    protected final int t;

    /**
     * 最大关键字数量2*t-1
     */
    private final Object[] keys;

    /**
     * 关键字数量需要满足t-1 <= n <= 2t-1
     */
    protected int n = 0;

    public BTreeNode(int t) {
        if (t < 2) {
            throw new IllegalArgumentException("t >= 2");
        }
        this.t = t;
        this.keys = new Object[2 * t - 1];
        this.n = 0;
    }

    protected void insertNotFull(K key) {
        Node<K> node = new Node<K>(-1, key, null, null);
        insert(node);
    }

    protected Node<K> spilt() {
        BTreeNode<K> lf = new BTreeNode<K>(t);
        BTreeNode<K> rt = new BTreeNode<K>(t);
        return spilt(lf, rt);
    }

    protected Node<K> spilt(BTreeNode<K> lf, BTreeNode<K> rt) {
        Assert.checkTrue(isFull(), "[" + this + "] is not full");
        K middle = getKey(t - 1);
        for (int i = 0; i < t - 1; i++) {
            lf.insert(getNode(i));
        }
        for (int i = t; i < 2 * t - 1; i++) {
            rt.insert(getNode(i));
        }
        return new Node<K>(-1, middle, lf, rt);
    }

    /**
     * 如果节点是内节点,左右子节点都只有t-1的关键字,左右子节点合并(情况2.c)
     *     当此内节点是叶子节点上一层的时候发现左右子节点只用t-1,左右子节点合并(3.b)
     * 如果节点是叶子节点,则直接删除(情况1) 节点节点不可能 出现less情况,如果有递归到父亲点点的时候已经删除
     */
    protected void remove(K key) {
        Assert.checkNotNull(key, "key");
        Node<K> node = getMergeNode(key);
        if (node.isLess()) {
            BTreeNode<K> merge = merge(node);
            remove(merge, node.getIndex());
            node = getMergeNode(key);
        }
        removeNotLess(key, node);
    }

    protected void removeNotLess(K key, Node<K> node) {
        Assert.checkTrue(!node.isLess(), "node  is  less");
        if (key.compareTo(node.getKey()) == 0) {
            remove(node.getIndex());
        }
    }

    protected BTreeNode<K> merge(Node node) {
        Assert.checkNotNull(node, "node");
        Assert.checkTrue(node.isLess(), "node  is not all less");
        BTreeNode<K> mergeNode = node.getLeft();
        BTreeNode<K> moveNode = node.getRight();
        mergeNode.append(node.getMergeNode());
        for (int i = 0; i < moveNode.n; i++) {
            mergeNode.append(moveNode.getNode(i));
        }
        return mergeNode;
    }

    protected K removeMaxKey() {
        return removeMaxNode().getKey();
    }

    protected Node<K> removeMaxNode() {
        Assert.checkTrue(n > 0, "n=0");
        Node<K> node = getNode(n - 1);
        remove(n - 1);
        return node;
    }

    protected K removeMinKey() {
        return removeMinNode().getKey();
    }

    protected Node<K> removeMinNode() {
        Assert.checkTrue(n > 0, "n=0");
        Node<K> node = getNode(0);
        remove(0);
        return node;
    }

    protected void append(Node<K> node) {
        insert(node, n);
    }

    protected void insert(Node<K> node) {
        Assert.checkNotNull(node, "node");
        Assert.checkNotNull(node.getKey(), "key");
        int index = findIndexFirstEqualOrMoreKey(node.getKey());
        insert(node, index);
    }

    protected void insert(Node<K> node, int index) {
        Assert.checkTrue(!isFull(), "node is full");
        Assert.checkNotNull(node, "node");
        Assert.checkNotNull(node.getKey(), "key");
        Assert.checkTrue(index < 2 * t - 1, "index[" + index + "] is out of array");
        for (int i = n - 1; i >= index; i--) {
            rightMove(i);
        }
        keys[index] = node.getKey();
        n++;
    }

    protected void replace(int index, K newKey) {
        keys[index] = newKey;
    }

    private void remove(int index) {
        remove(null, index);
    }

    protected void remove(BTreeNode<K> rt, int index) {
        Assert.checkTrue(index >= 0 && index < n, "index[" + index + "] is out of array");
        for (int i = index + 1; i < n; i++) {
            leftMove(i);
        }
        n--;
    }

    protected void rightMove(int index) {
        keys[index + 1] = keys[index];
        keys[index] = null;
    }

    protected void leftMove(int index) {
        Assert.checkTrue(index >= 0, "index[" + index + "] is out of array");
        keys[index - 1] = keys[index];
        keys[index] = null;
    }

    protected int findIndexFirstLessKey(K key) {
        return findIndexFromRight(key, -1);
    }

    protected int findIndexFirstMoreKey(K key) {
        return findIndexFromLeft(key, 1);
    }

    protected int findIndexEqualKey(K key) {
        return findIndexFromRight(key, 0);
    }

    protected int findIndexFirstEqualOrMoreKey(K key) {
        return findIndexFirstLessKey(key) + 1;
    }

    protected int findIndexFirstEqualOrLessKey(K key) {
        return findIndexFirstMoreKey(key) - 1;
    }

    protected Node<K> getMergeNode(K key) {
        int index = findIndexFirstEqualOrLessKey(key);
        if (index == -1) {
            return getNode(0);
        }
        return getNode(index);
    }

    protected BTreeNode<K> getSplitChild(K key) {
        int index = findIndexFirstEqualOrMoreKey(key);
        return getLeftChild(index);
    }

    private int findIndexFromRight(K key, int value) {
        Assert.checkNotNull(key, "key");
        Assert.checkTrue(value > -2 && value < 1, "value only be [-1,0]");
        for (int i = n - 1; i >= 0; i--) {
            if (getKey(i).compareTo(key) == value) {
                return i;
            }
        }
        return -1;
    }

    private int findIndexFromLeft(K key, int value) {
        Assert.checkNotNull(key, "key");
        Assert.checkTrue(value > -1 && value < 2, "value only be [1,0]");
        for (int i = 0; i < n; i++) {
            if (getKey(i).compareTo(key) == value) {
                return i;
            }
        }
        return n;
    }

    protected Node<K> getNode(int index) {
        return new Node<K>(index, getKey(index), getLeftChild(index), getRightChild(index));
    }

    @SuppressWarnings("unchecked")
    protected K getKey(int index) {
        return (K) keys[index];
    }

    protected BTreeNode<K> getLeftChild(int index) {
        return null;
    }

    protected BTreeNode<K> getRightChild(int index) {
        return null;
    }

    protected boolean isFull() {
        return this.n == 2 * t - 1;
    }

    protected boolean isLess() {
        return this.n == t - 1;
    }

    protected boolean isLeaf() {
        return true;
    }

    @Override
    public String toString() {
        return toString(0);
    }

    protected String toString(int lev) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            sb.append(getLevSpace(lev)).append(keys[i]).append("\n");
        }
        return sb.toString();
    }

    protected String getLevSpace(int lev) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < lev; i++) {
            sb.append("   ");
        }
        return sb.toString();
    }

    protected final static class Node<K extends Comparable<K>> {
        private int index = -1;
        private K key;
        private BTreeNode<K> left;
        private BTreeNode<K> right;

        public Node(int index, K key, BTreeNode<K> left, BTreeNode<K> right) {
            Assert.checkNotNull(key, "key");
            this.index = index;
            this.key = key;
            this.left = left;
            this.right = right;
        }

        int getIndex() {
            return index;
        }

        K getKey() {
            return key;
        }

        BTreeNode<K> getLeft() {
            return left;
        }

        BTreeNode<K> getRight() {
            return right;
        }

        public boolean isLess() {
            if (right != null && left != null) {
                return right.isLess() && left.isLess();
            }
            return false;
        }

        public Node<K> getMergeNode() {
            Assert.checkTrue(isLess(), "node  is not  less");
            BTreeNode<K> lf = left.getRightChild(left.n - 1);
            BTreeNode<K> rt = right.getLeftChild(0);
            return new Node<K>(left.n, key, lf, rt);
        }
    }
}
public class Assert {


    public static  void checkTrue(boolean b,String message){
        if(!b)
            throw new RuntimeException(message);
    }

    public static  void checkNotNull(Object obj,String name){
        checkTrue(obj!=null,name+" is  null");
    }
    public static  void checkNull(Object obj,String name){
        checkTrue(obj==null,name+" is not null");
    }
    
}

B+Tree

B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构。

B+Tree相对于B-Tree有几点不同:

  1. 非叶子节点只存储键值信息。
  2. 所有叶子节点之间都有一个链指针。
  3. 数据记录都存放在叶子节点中。

数据为什么要用B+树而不用B树?

1.B-Tree中可每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率

2.sql 有有些范围查找比如    5>id> 3,  B树的结构要查出3到5之间的数据可能要回溯的根节点,可B+只要找到 5,3,2个节点再取其中间的数据就行
 

参考文章

  <算法导论>书

【算法导论】B树

BTree和B+Tree详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值