《算法—深入淺出》紅黑樹(shù)的旋轉(zhuǎn)

一撞秋、《算法—深入淺出》N叉樹(shù)的介紹
二拱层、《算法—深入淺出》紅黑樹(shù)的旋轉(zhuǎn)
三抽减、《算法—深入淺出》紅黑樹(shù)的插入
四、《算法—深入淺出》紅黑樹(shù)的刪除

一、前言

紅黑樹(shù)(R-B Tree)佳簸,它是二叉樹(shù)中,最經(jīng)典也是最復(fù)雜的數(shù)據(jù)結(jié)構(gòu)颖变。

\color{red}{紅黑樹(shù)的應(yīng)用:}

  • 廣泛應(yīng)用于 C++ 的 STL 中生均, map 和 set 是用紅黑樹(shù)實(shí)現(xiàn)的;
  • Linux 的進(jìn)程調(diào)度腥刹,使用紅黑樹(shù)管理進(jìn)程控制塊马胧,進(jìn)程的虛擬內(nèi)存空間都存儲(chǔ)在一顆紅黑樹(shù)上,每個(gè)虛擬內(nèi)存空間都對(duì)應(yīng)紅黑樹(shù)的一個(gè)結(jié)點(diǎn)衔峰,左指針指向相鄰的虛擬內(nèi)存空間佩脊,右指針指向相鄰的高地址虛擬內(nèi)存空間;
  • IO 多路復(fù)用的 epoll 采用紅黑樹(shù)組織管理 sockfd朽色,以支持快速的增刪改查邻吞;
  • Nginx 中采用紅黑樹(shù)管理定時(shí)器,因?yàn)榧t黑樹(shù)是有序的葫男,可以很快的得到距離當(dāng)前最小的定時(shí)器抱冷;
  • Java 中的 TreeMap 和 TreeSet 也是采用紅黑樹(shù)實(shí)現(xiàn);JDK8開(kāi)始梢褐,HashMap 也新增了紅黑樹(shù)旺遮,鏈表與紅黑樹(shù)可以互轉(zhuǎn);

\color{red}{紅黑樹(shù)的特性:}

  1. 每個(gè)節(jié)點(diǎn)或者是黑色盈咳,或者是紅色耿眉;
  2. 根節(jié)點(diǎn)是黑色;
  3. 每個(gè)葉子節(jié)點(diǎn)(NIL)是黑色鱼响; 【注意:這里葉子節(jié)點(diǎn)鸣剪,是指為空(NIL或NULL)的葉子節(jié)點(diǎn)!】
  4. 如果一個(gè)節(jié)點(diǎn)是紅色的丈积,則它的子節(jié)點(diǎn)必須是黑色的筐骇;
  5. 從一個(gè)節(jié)點(diǎn)到該節(jié)點(diǎn)的子孫節(jié)點(diǎn)的所有路徑上包含相同數(shù)目的黑節(jié)點(diǎn);

它的復(fù)雜在于:

  • 插入節(jié)點(diǎn)時(shí)江滨,可能違反規(guī)定铛纬,從而需要遞歸來(lái)【旋轉(zhuǎn)+重新著色】;
  • 刪除節(jié)點(diǎn)時(shí)唬滑,與插入節(jié)點(diǎn)類似的調(diào)整告唆,只是在調(diào)整前棺弊,需要先找到一個(gè)后繼節(jié)點(diǎn)來(lái)替換;

因此擒悬,我們可以看到模她,最復(fù)雜的調(diào)整,是刪除節(jié)點(diǎn)后的操作茄螃!

本篇不談插入與刪除菜秦,而是先打基礎(chǔ):\color{red}{旋轉(zhuǎn)}绰寞!
無(wú)論是插入還是刪除起愈,當(dāng)要調(diào)整時(shí)麸恍,都會(huì)旋轉(zhuǎn)子樹(shù),然后再向上遞歸拼弃,直至最終滿足紅黑樹(shù)的特性夏伊。

二、旋轉(zhuǎn)

左旋是基于某個(gè)節(jié)點(diǎn)吻氧,整體(該節(jié)點(diǎn)連同其左溺忧、右子樹(shù))向左旋轉(zhuǎn);
右旋是基于某個(gè)節(jié)點(diǎn)盯孙,整體(該節(jié)點(diǎn)連同其左鲁森、右子樹(shù))向右旋轉(zhuǎn);

2.1振惰、數(shù)據(jù)結(jié)構(gòu)

public class RBTree {
    private TreeNode root;

    public RBTree(TreeNode root) {
        this.root = root;
    }

    public static class TreeNode {
        public static final int BLACK = 0;
        public static final int RED = 1;

        public int value;
        public int color = BLACK;
        public TreeNode parent;
        public TreeNode left;
        public TreeNode right;

        public TreeNode(int value) {
            this.value = value;
        }

        public TreeNode(int value, TreeNode parent) {
            this.value = value;
            this.parent = parent;
        }
    }
}

2.2歌溉、生成二叉樹(shù)

{
    private TreeNode getTree() {
        TreeNode head = new TreeNode(10);
        head.left = new TreeNode(5, head);
        head.left.left = new TreeNode(2, head.left);
        head.left.right = new TreeNode(8, head.left);
    
        head.right = new TreeNode(20, head);
        head.right.left = new TreeNode(16, head.right);
        head.right.right = new TreeNode(22, head.right);
        return head;
    }
    
    // 前序打印方法
    private void doPreTravel(TreeNode node) {
        if (node == null) {
            return;
        }
    
        System.out.println((node.parent == null ? "(root)" : "") + node.value);
        doPreTravel(node.left);
        doPreTravel(node.right);
    }
}

// doPreTravel(getTree())
// 打印輸出(前序遍歷: 根 -> 左子樹(shù) -> 右子樹(shù))
// 10, 5, 2, 8, 20, 16, 22

如下圖(我們后面的旋轉(zhuǎn)都基于 10 這個(gè)根節(jié)點(diǎn)來(lái)旋轉(zhuǎn))


R-B Tree.png

2.3、左旋(算法只考慮旋轉(zhuǎn)骑晶,暫不管著色)

R-B Tree Rotate Left.png
public class RBTree {
    private TreeNode root;
    /*****************************************************************************************************************
     *   (1)                                                      |  (2)
     *              g(10)                          u(20)          |         g            g
     *            /      \                       /     \          |        /            /
     *        p(5)       u(20)        =>     g(10)     z(22)      |       p     =>     u
     *       /   \       /    \             /    \                |        \          /
     *      c(2) x(8)   y(16) z(22)      p(5)    y(16)            |         u        p
     *                                  /   \                     |
     *                               c(2)   x(8)                  |
     *                                                            |
     *****************************************************************************************************************/
    public void rotateLeft(TreeNode parent) {
        TreeNode right = parent.right;  // u

        //【右子左節(jié)點(diǎn)】掛到【父的右節(jié)點(diǎn)】
        parent.right = right.left;      // g.右節(jié)點(diǎn) = y
        if (right.left != null) {
            right.left.parent = parent; // y.父節(jié)點(diǎn) = g
        }

        // 【祖父】與【左子 or 右子】
        right.parent = parent.parent;   // u.父節(jié)點(diǎn) = g.父節(jié)點(diǎn)
        if (parent.parent != null) {
            if (parent.parent.left == parent) {
                parent.parent.left = right; // 祖父節(jié)點(diǎn).左節(jié)點(diǎn) = u
            } else {
                parent.parent.right = right;
            }
        } else {
            root = right;
        }

        // 【父】與【右子】替換
        parent.parent = right;          // g.父節(jié)點(diǎn) = u
        right.left = parent;            // u.左節(jié)點(diǎn) = g
    }
}

2.4痛垛、右旋(算法只考慮旋轉(zhuǎn),暫不管著色)

R-B Tree Rotate Right.png
public class RBTree {
    private TreeNode root;
    /*****************************************************************************************************************
     *  (1)                                                                 |  (2)
     *              g(10)                          p(5)                     |           p           g
     *            /      \                       /     \                    |          /           / \
     *        p(5)       u(20)        =>     c(2)       g(10)               |         g     =>    x   p
     *       /   \       /    \                        /    \               |        /
     *      c(2) x(8)   y(16) z(22)                  x(8)    u(20)          |       x
     *                                                      /    \          |
     *                                                     y(16) z(22)      |
     *                                                                      |
     *****************************************************************************************************************/
    private void rotateRight(TreeNode parent) {
        TreeNode left = parent.left;    // p

        // 【左子右節(jié)點(diǎn)】掛到【父的左節(jié)點(diǎn)】
        parent.left = left.right;       // g.左節(jié)點(diǎn) = x
        if (left.right != null) {
            left.right.parent = parent; // x.父節(jié)點(diǎn) = g
        }

        // 【祖父】與【左子 or 右子】
        left.parent = parent.parent;    // p.父節(jié)點(diǎn) = g.父節(jié)點(diǎn)
        if (parent.parent != null) {
            if (parent.parent.left == parent) {
                parent.parent.left = left;
            } else {
                parent.parent.right = left;
            }
        } else {
            root = left;
        }

        // 【父】與【左子】替換
        parent.parent = left;           // g.父節(jié)點(diǎn) = p
        left.right = parent;            // p.右節(jié)點(diǎn) = g
    }
}

詳細(xì)請(qǐng)查看 Github 源碼:《紅黑樹(shù)完整的源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末桶蛔,一起剝皮案震驚了整個(gè)濱河市匙头,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌仔雷,老刑警劉巖蹂析,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異碟婆,居然都是意外死亡电抚,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門脑融,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)喻频,“玉大人缩宜,你說(shuō)我怎么就攤上這事肘迎∩拢” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵妓布,是天一觀的道長(zhǎng)姻蚓。 經(jīng)常有香客問(wèn)我,道長(zhǎng)匣沼,這世上最難降的妖魔是什么狰挡? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮释涛,結(jié)果婚禮上加叁,老公的妹妹穿的比我還像新娘。我一直安慰自己唇撬,他們只是感情好它匕,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著窖认,像睡著了一般豫柬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扑浸,一...
    開(kāi)封第一講書(shū)人閱讀 52,475評(píng)論 1 312
  • 那天烧给,我揣著相機(jī)與錄音,去河邊找鬼喝噪。 笑死础嫡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的仙逻。 我是一名探鬼主播驰吓,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼系奉!你這毒婦竟也來(lái)了檬贰?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤缺亮,失蹤者是張志新(化名)和其女友劉穎翁涤,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體萌踱,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡葵礼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了并鸵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸳粉。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖园担,靈堂內(nèi)的尸體忽然破棺而出届谈,到底是詐尸還是另有隱情枯夜,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布艰山,位于F島的核電站湖雹,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏曙搬。R本人自食惡果不足惜摔吏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望纵装。 院中可真熱鬧征讲,春花似錦、人聲如沸橡娄。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瀑踢。三九已至扳还,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間橱夭,已是汗流浹背氨距。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留棘劣,地道東北人俏让。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像茬暇,于是被迫代替她去往敵國(guó)和親首昔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

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