紅黑樹+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個數特別大的。

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