红黑树+B树

红黑树的概念:
红黑数,是一种二叉搜索树,但在每一个节点上增加一个存储位表示节点的颜色,可以是Red或Black。通过对任意一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。
红黑树的性质(规则):
1.每个节点不是红色就是黑色
2.根节点是黑色
3.如果一个节点是红色的,则它的两个孩子节点是黑色的(红色不能挨红色
4.对于每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
5.每个叶子节点都是黑色的(此处的叶子节点指的是空节点)
满足以上性质:红黑树就能保证:其最长路径中节点个数不会超过最短路径个数的两倍。
在这里插入图片描述
平衡树的目的:只是让二叉树的高度尽可能的低,不要退化成单支数即可。

插入操作:
1.按照普通搜索树的方式进行插入
2.只会插入红色的节点(有意识的破坏红红不能相邻的规则)重点
3.检查红色是否是相邻的?
----1.如果我的父亲是黑色的,就没有破坏规则。插入结束
----2.如果我的父亲是红色的,红红相邻了,我们需要修复。
提问:我的祖父是什么颜色?
一定是黑色的,因为已经确定父亲是红色的,如果祖父不为红,则表示之前就已经触发了红红相邻。
4.检查根是否是黑色的。
在这里插入图片描述
一组随机数的调整:
75,147,166,97,12,154,26,84,83,100,78,170,37,58,128,146,143,183,91
在这里插入图片描述
红黑树大的规则:

  1. 非红即黑
  2. 收尾皆黑
  3. 红不相邻
  4. 黑色同数

L(最长的路径)<= 最短路径*2

近似于平衡树,可以降低 插入/查找/删除的时间复杂度(log(N))

怎么插入?代码如何实现?


public class Node {
    enum Color{
        Red,
        Black
    }
    Node left;
    Node right;
    Node parent;
    int key;
    Color color=Color.Red;
    public Node (int key){
        this.key = key;
    }
}

public class Tree {
    Node root;
    void insert(int key){
        if (root==null){
            root=new Node(key);
            root.color =Node.Color.Black;
            return;
        }
        //按照搜索树的方法插入
        Node parent = null;
        Node cur=root;
        while (cur!=null){
            if (key==cur.key){
                System.out.println("插入重复:"+key);
                return;
            }else if (key<cur.key){
                parent=cur;
                cur=cur.left;
            }else {
                parent=cur;
                cur = cur.right;
            }
        }
        if (key<parent.key){
            cur=parent.left=new Node(key);
            cur.parent =parent;
        }else {
            cur=parent.right = new Node(key);
            cur.parent = parent;
        }
        while (parent!=null&&parent.color==Node.Color.Red&&parent.parent!=null){
            Node grandpa=parent.parent;
            if (parent==grandpa.left){
                //左
                Node uncle=grandpa.right;
                if (uncle!=null&&uncle.color==Node.Color.Red){
                    grandpa.color=Node.Color.Red;
                    parent.color=uncle.color=Node.Color.Black;
                    cur=grandpa;
                    parent=grandpa.parent;
                }else {
                    //此时uncle为null或者color为黑色
                    //左右的情况
                    if (cur==parent.right){
                        leftRotate(parent);
                        Node tmp=cur;
                        cur=parent;
                        parent=tmp;
                    }
                    //走到这不管有没有经历过左旋 都是左左的情况
                    rightRotate(grandpa);
                    grandpa.color = Node.Color.Red;
                    parent.color=Node.Color.Black;
                    //当我们的目前子树的根节点为黑的时候就不需要在做调整了
                    break;
                }
            }else {
                //右
                Node uncle=grandpa.left;
                if (uncle!=null&&uncle.color==Node.Color.Red){
                    grandpa.color=Node.Color.Red;
                    parent.color=uncle.color=Node.Color.Black;
                    cur=grandpa;
                    parent=grandpa.parent;
                }else {
                    //此时uncle为null或者color为黑色
                    //右左的情况
                    if (cur==parent.left){
                        rightRotate(parent);
                        Node tmp=cur;
                        cur=parent;
                        parent=tmp;
                    }
                    //走到这不管有没有经历过左旋 都是左左的情况
                    leftRotate(grandpa);
                    grandpa.color = Node.Color.Red;
                    parent.color=Node.Color.Black;
                    //当我们的目前子树的根节点为黑的时候就不需要在做调整了
                    break;
                }
            }
        }
        root.color=Node.Color.Black;

    }

    private void rightRotate(Node parent) {
        Node cur=parent.left;
        Node rightOfCur=cur.right;
        Node grandpa=parent.parent;

        cur.right=parent;
        cur.parent=grandpa;


        parent.parent=cur;
        parent.left=rightOfCur;

        if (grandpa!=null){
            if (parent==grandpa.left){
                grandpa.left =cur;
            }else {
                grandpa.right=cur;
            }
        }else {
            root=cur;
        }
        if (rightOfCur!=null){
            rightOfCur.parent=parent;
        }

    }

    private void leftRotate(Node parent) {
        Node cur=parent.right;
        Node leftOfCur=cur.left;
        Node grandpa=parent.parent;

        parent.parent=cur;
        parent.right=leftOfCur;
        cur.parent=grandpa;
        cur.left=parent;
        if (grandpa!=null){
            if (parent==grandpa.left){
                grandpa.left=cur;
            }else {
                grandpa.right=cur;
            }
        }else {
            root=cur;
        }
        if (leftOfCur!=null){
            leftOfCur.parent=parent;
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Random random=new Random(20200219);
        Tree tree=new Tree();
        for (int i = 0; i < 20; i++) {
            int key=random.nextInt(200);
            System.out.println(key);
            tree.insert(key);
        }
        System.out.println("插入成功");
    }
}

B-树:多叉搜索树

多叉搜索树:
1.可以保存多个key和多个孩子
2.节点中保存的key有序
3.规定一个值M
4.如果B-树是M阶树,含义:

  1. 除根之外,节点中最多的有M-1个key
  2. 除根之外,节点中最多有M个child

5 .无论何时,child总比key多一个。
在这里插入图片描述
查找:
每个节点中有一组Key(有序)
遍历查找即可(一般来说数组的长度有限-1024)
如果找到了,返回节点和下标
如果没有找到,应该会对应一个孩子的位置
继续去孩子节点中找
如果孩子是null,表示key不在树中
在这里插入图片描述
插入操作:

我们进行试着以2-3树插入一组数据 { 97,38,75,65,93,29,25,36,3,25,67,86,80,36,89,62,31,6,4,77 }
在这里插入图片描述
B-树的概念:

  1. 根节点至少有两个孩子(根中至少有个key)
  2. 每个非根节点至少有M/2(上取整)个孩子,至多有M个孩子
    分裂时自然保证了
    index是中间位置的key往上走,
    左部分留在当前节点,
    右部分创建新节点,搬家
    分家后,节点中至少还有一半个孩子
  3. 每个非根节点至少有M/2-1(上取整)个关键字,至多有M-1个关键字,并且以升序排列
  4. key[ i ]和key[ i+1 ]之间的孩子节点的值介于key[ i ],key[ i+1 ]之间
  5. 所有的叶子节点都在同一层
    插入只往叶子节点插,树是往上生长的。

插入的规则:只往叶子里插入,根据刚才的规则,找到和是的叶子

  1. 判断是否满足key < M-1 的规则,如果满足,插入完毕
  2. 如果不满足,开始准备分裂

分裂:

  1. 创建一个新的节点
  2. 把midindex往右的所有key连同child一起搬到新的节点 rightPart
  3. midIndex往左的所有key连同child留在原节点不动
  4. 把midIndex所在位置的key按照方式插入到父节点上去,同时key右边指向rightPart
    如果父节点为null,创建新节点 [ 0 ]是原节点[ 1 ]是rightPart
    插入时,按照插入排序当时的方式key往后搬,记着把child也往后搬

现在再次以6-7树我们再次插入一次元素:{ 97,38,75,65,93,29,25,36,3,25,67,86,80,36,89,62,31,6,4,77 }
在这里插入图片描述
M阶 B-树:每个节点中最多能有M-1个key
B-树 的第一层:M-1个key
B-树 的第二层: M*(M-1)个key
第三次估算有: M^3个key, 第四次估算有: M^4个key
按这样增长,可存key个数特别大的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章