數(shù)據(jù)結(jié)構(gòu)--紅黑樹

2-3樹
2-3樹
  • 滿足二分搜索樹的基本性質(zhì)
  • 節(jié)點(diǎn)可以存放一個元素或者兩個元素
  • 每個節(jié)點(diǎn)可以有2個孩子(二節(jié)點(diǎn)) 或者3個孩子(三節(jié)點(diǎn))
  • 絕對平衡的樹(從根節(jié)點(diǎn)到任意一個葉子節(jié)點(diǎn)所通過的節(jié)點(diǎn)數(shù)量一定是相同的)
2-3樹保持平衡

添加節(jié)點(diǎn)永遠(yuǎn)不會去空的節(jié)點(diǎn)颜矿,會和最后找到的葉子節(jié)點(diǎn)做融合搬瑰。

添加12
添加2
添加4
添加4

紅黑樹

等價于2-3樹

  • 保持黑平衡的二叉樹莺丑,左右子樹的黑色節(jié)點(diǎn)的高度差保持絕對平衡猜揪,2-3樹本身是保持絕對平衡的
  • 每個節(jié)點(diǎn)或者是紅色的闪幽,后者是黑色的
  • 根節(jié)點(diǎn)是黑色的
  • 每一個葉子節(jié)點(diǎn)(最后的空節(jié)點(diǎn))是黑色的
  • 如果一個節(jié)點(diǎn)是紅色贱枣,它的孩子節(jié)點(diǎn)都是黑色月洛,黑色節(jié)點(diǎn)的右孩子一定是黑色的
  • 從任意一個節(jié)點(diǎn)出發(fā)到葉子節(jié)點(diǎn)藏研,經(jīng)過的黑色節(jié)點(diǎn)是一樣的
  • 所有的紅色節(jié)點(diǎn)向左傾斜
  • 最大高度:2logn
紅黑樹與2-3樹
//紅黑樹基于二分搜索樹
public class RedBlackTree <K extends Comparable<K>, V>{

    private static final boolean RED = true;
    private static final boolean Black = false;

    private class Node{
        public K key;
        public V value;
        public Node left, right;
        public boolean color;

        public Node(K key, V value){
            this.key = key;
            this.value = value;
            left = null;
            right = null;
            color = RED;//因?yàn)樵?-3樹中添加的節(jié)點(diǎn)永遠(yuǎn)都是要融合到已有的節(jié)點(diǎn)中
        }
    }

    private Node root;
    private int size;

    public RedBlackTree(){
        root = null;
        size = 0;
    }

    public int getSize() {
        return size;
    }


    public boolean isEmpty() {
        return size == 0;
    }


    //判斷node顏色
    private boolean isRed(Node node) {
        if (node == null)
            return Black;
        return node.color;
    }

    public boolean contains(K key) {
        return getNode(root, key) != null;
    }


    public V get(K key) {
        Node node = getNode(root, key);
        return node == null? null : node.value;
    }


    public void set(K key, V newValue) {
        Node node = getNode(root, key);
        if (node == null)
            throw new IllegalArgumentException(key + "doesn`t exist");
        node.value = newValue;
    }

}
添加元素相關(guān)操作
  • 左旋轉(zhuǎn),當(dāng)添加的元素大于節(jié)點(diǎn)時咐汞,進(jìn)行左旋轉(zhuǎn)盖呼。(紅色節(jié)點(diǎn)需要向左傾斜)
添加42

左旋轉(zhuǎn)

左旋轉(zhuǎn)代碼

    //     node                                x
    //    /   \           左旋轉(zhuǎn)             /    \
    //   T1    x      ----------->       node    T3
    //       /  \                       /   \
    //      T2  T3                     T1   T2
    //左旋轉(zhuǎn)
    private Node leftRotate(Node node) {

        Node x = node.right;
        //左旋轉(zhuǎn)
        node.right = x.left;
        x.left = node;

        x.color = node.color;
        node.color = RED; //是2-3樹中的三節(jié)點(diǎn)

        return x;
    }
  • 顏色翻轉(zhuǎn),添加66化撕,因?yàn)?2需要繼續(xù)向上融合(向紅黑樹中的3節(jié)點(diǎn)添加元素)
添加66
顏色翻轉(zhuǎn)

顏色翻轉(zhuǎn)代碼

    private void flipColors(Node node) {
        node.color = RED; //由于需要繼續(xù)向上融合 所以是紅色 
        node.left.color = Black;  // 三個二節(jié)點(diǎn) 是黑色
        node.right.color = Black; // 三個二節(jié)點(diǎn) 是黑色
    }
  • 右旋轉(zhuǎn)几晤,需要翻轉(zhuǎn)顏色
添加12
右旋轉(zhuǎn)
顏色翻轉(zhuǎn)

右旋轉(zhuǎn)代碼

    //右旋轉(zhuǎn)
    //        node                           x
    //        /   \       右旋轉(zhuǎn)            /   \
    //       x    T2  -------------->     y    node
    //     /  \                               /   \
    //    y   T1                             T1   T2
    private Node rightRotate(Node node) {
        Node x = node.left;

        //右旋轉(zhuǎn)
        node.left = x.right;
        x.right = node;

        x.color = node.color;
        node.color = RED;

        return x;
    }
  • 先左旋轉(zhuǎn)在右旋轉(zhuǎn)在翻轉(zhuǎn)顏色


    添加中間值元素
先左旋轉(zhuǎn)
再右旋轉(zhuǎn)
顏色翻轉(zhuǎn)
添加元素過程總結(jié)
添加元素維護(hù)邏輯
    //向紅黑樹中添加元素
    public void add(K key, V value) {
        root = add(root, key, value);
        root.color = Black;  //最終的根節(jié)點(diǎn)為黑色節(jié)點(diǎn)
    }


    //以向node為根的紅黑樹中插入元素(key, value), 遞歸算法
    //返回插入新節(jié)點(diǎn)后紅黑樹的根
    private Node add(Node node, K key, V value) {
        if (node == null) {
            size ++;
            return new Node(key, value); // 默認(rèn)插入紅色節(jié)點(diǎn)
        }

        //如果相等 則不作處理
        if (key.compareTo(node.key) < 0)
            node.left = add(node.left, key, value);
        else if (key.compareTo(node.key) > 0)
            node.right = add(node.right, key, value);
        else // ==
            node.value = value;

        //右孩子是紅色 左孩子不是紅色的
        if (isRed(node.right) && !isRed(node.left))
            node = leftRotate(node);

        //左孩子 以及 左孩子的左孩子都是紅色的
        if (isRed(node.left) && isRed(node.left.left))
            node = rightRotate(node);

        //左孩子 與 右孩子 都是紅色的
        if (isRed(node.left) && isRed(node.right))
            flipColors(node);

        return node;
    }

性能總結(jié)

對于完全隨機(jī)的數(shù)據(jù),普通的二分搜索樹很好用
缺點(diǎn):極端情況下退化成鏈表(或者高度不平衡)
對于查詢較多的使用情況植阴,AVL樹很好用
紅黑樹犧牲了平衡性(2logn的高度)
紅黑樹統(tǒng)計性能更優(yōu)(綜合增刪改查所有的操作)蟹瘾,平均性能優(yōu)于AVL樹。
數(shù)據(jù)結(jié)構(gòu)中經(jīng)常發(fā)生添加掠手,刪除的情況時憾朴,紅黑樹更合適。查詢并不占優(yōu)勢喷鸽。
如果數(shù)據(jù)近乎不會動的話众雷,只查詢,AVL樹性能更好做祝。

時間復(fù)雜度: O(logn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末砾省,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子剖淀,更是在濱河造成了極大的恐慌纯蛾,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纵隔,死亡現(xiàn)場離奇詭異翻诉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)捌刮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門碰煌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绅作,你說我怎么就攤上這事芦圾。” “怎么了俄认?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵个少,是天一觀的道長洪乍。 經(jīng)常有香客問我,道長夜焦,這世上最難降的妖魔是什么壳澳? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮茫经,結(jié)果婚禮上巷波,老公的妹妹穿的比我還像新娘。我一直安慰自己卸伞,他們只是感情好抹镊,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著荤傲,像睡著了一般垮耳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遂黍,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天氨菇,我揣著相機(jī)與錄音,去河邊找鬼妓湘。 笑死,一個胖子當(dāng)著我的面吹牛乌询,可吹牛的內(nèi)容都是我干的榜贴。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼妹田,長吁一口氣:“原來是場噩夢啊……” “哼唬党!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鬼佣,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤驶拱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后晶衷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蓝纲,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年晌纫,在試婚紗的時候發(fā)現(xiàn)自己被綠了税迷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡锹漱,死狀恐怖箭养,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情哥牍,我是刑警寧澤毕泌,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布喝检,位于F島的核電站,受9級特大地震影響撼泛,放射性物質(zhì)發(fā)生泄漏挠说。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一坎弯、第九天 我趴在偏房一處隱蔽的房頂上張望纺涤。 院中可真熱鬧,春花似錦抠忘、人聲如沸撩炊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拧咳。三九已至,卻和暖如春囚灼,著一層夾襖步出監(jiān)牢的瞬間骆膝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工灶体, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留阅签,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓蝎抽,卻偏偏與公主長得像政钟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子樟结,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容