自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

數(shù)據(jù)結(jié)構(gòu):紅黑樹實(shí)現(xiàn)原理,從0基礎(chǔ)解釋到底層代碼實(shí)現(xiàn)手寫

開發(fā) 前端
希望這篇文章能幫助到想學(xué)習(xí)紅黑樹結(jié)構(gòu)的朋友,目標(biāo):理解紅黑樹,并能實(shí)現(xiàn)手寫代碼。

什么是紅黑樹?

紅黑樹是一種自平衡的二叉查找樹,是一種高效的查找樹。它是由 Rudolf Bayer 于1972年發(fā)明,在當(dāng)時(shí)被稱為對(duì)稱二叉 B 樹(symmetric binary B-trees)。后來,在1978年被 Leo J. Guibas 和 Robert Sedgewick 修改為如今的紅黑樹。紅黑樹具有良好的效率,它可在 O (logN) 時(shí)間內(nèi)完成查找、增加、刪除等操作。因此,紅黑樹在業(yè)界應(yīng)用很廣泛,比如 Java 中的 TreeMap,JDK 1.8中的 HashMap、C++ STL 中的 map 均是基于紅黑樹結(jié)構(gòu)實(shí)現(xiàn)的。

簡(jiǎn)單介紹一下什么是O(logN)

當(dāng)我們談?wù)撍惴ǖ男蕰r(shí),我們通常使用時(shí)間復(fù)雜度來描述算法的運(yùn)行時(shí)間與輸入規(guī)模之間的關(guān)系。時(shí)間復(fù)雜度以大O符號(hào)(O)表示。

時(shí)間復(fù)雜度 O(logN)

在時(shí)間復(fù)雜度中,O(logN) 是一種非常高效的情況。它表示算法的運(yùn)行時(shí)間與輸入規(guī)模的對(duì)數(shù)成正比。這意味著隨著輸入規(guī)模的增加,算法的運(yùn)行時(shí)間將以對(duì)數(shù)的速度增長。

具體來說,假設(shè)一個(gè)算法的時(shí)間復(fù)雜度是 O(logN),其中 N 代表輸入規(guī)模。當(dāng) N 增加時(shí),該算法的運(yùn)行時(shí)間將以對(duì)數(shù)的速度增加。這意味著隨著輸入規(guī)模的翻倍,該算法的運(yùn)行時(shí)間僅會(huì)略微增加。

O(logN) 的高效性來自于對(duì)數(shù)函數(shù)的特性。對(duì)數(shù)函數(shù)具有一個(gè)很快的增長速度,但在開始時(shí)增長很快,隨著輸入的增加,增長速度逐漸減緩。這使得算法能夠在處理大規(guī)模輸入時(shí)保持相對(duì)較低的運(yùn)行時(shí)間。

紅黑樹的基本概念(應(yīng)付面試官)

紅黑樹是一種特殊的二叉查找樹,它除了滿足二叉查找樹的基本性質(zhì)外,還具有以下幾個(gè)特點(diǎn):

  • 每個(gè)節(jié)點(diǎn)都有一個(gè)顏色屬性,要么是紅色,要么是黑色。
  • 根節(jié)點(diǎn)的顏色是黑色。
  • 所有葉子節(jié)點(diǎn)(NIL節(jié)點(diǎn))的顏色都是黑色。
  • 如果一個(gè)節(jié)點(diǎn)是紅色,那么它的兩個(gè)子節(jié)點(diǎn)都是黑色。
  • 從任意一個(gè)節(jié)點(diǎn)到其所有后代葉子節(jié)點(diǎn)的簡(jiǎn)單路徑上,包含相同數(shù)量的黑色節(jié)點(diǎn)。

這些性質(zhì)保證了紅黑樹在插入和刪除操作后能夠自動(dòng)調(diào)整,使得樹保持平衡狀態(tài)。平衡狀態(tài)指的是從根節(jié)點(diǎn)到葉子節(jié)點(diǎn)的最長路徑不超過最短路徑的兩倍。這樣就避免了二叉查找樹在極端情況下退化成鏈表,導(dǎo)致效率降低。

紅黑樹的性質(zhì)

為了方便分析,我們定義以下幾個(gè)概念:

  • 黑高(black-height):從某個(gè)節(jié)點(diǎn)出發(fā)(不包括該節(jié)點(diǎn))到達(dá)一個(gè)葉子節(jié)點(diǎn)的任意一條路徑上,黑色節(jié)點(diǎn)的個(gè)數(shù)稱為該節(jié)點(diǎn)的黑高,記為 bh(x)。
  • 紅黑性質(zhì)(red-black property):指紅黑樹滿足上述五個(gè)性質(zhì)。
  • 紅黑樹(red-black tree):一棵滿足紅黑性質(zhì)的二叉查找樹。

有了這些定義,我們可以推導(dǎo)出以下幾個(gè)重要的性質(zhì):

  • 性質(zhì)1:一棵有 n 個(gè)內(nèi)部節(jié)點(diǎn)(非 NIL 節(jié)點(diǎn))的紅黑樹,其高度 h 不超過 2log(n+1)。證明:設(shè) x 是紅黑樹中任意一個(gè)內(nèi)部節(jié)點(diǎn),h(x) 是以 x 為根的子樹的高度,n(x) 是以 x 為根的子樹內(nèi)部節(jié)點(diǎn)的個(gè)數(shù)。由于 x 的兩個(gè)子節(jié)點(diǎn)至少有一個(gè)是黑色(性質(zhì)4),所以 h(x) >= bh(x)。另一方面,由于從 x 到其后代葉子節(jié)點(diǎn)的任意路徑上至少有一半是黑色(性質(zhì)5),所以 bh(x) >= h(x)/2。綜合起來,有 h(x) >= bh(x) >= h(x)/2。由此可得 h(x) <= 2bh(x)。對(duì)于整棵樹而言,設(shè)其高度為 h,內(nèi)部節(jié)點(diǎn)個(gè)數(shù)為 n,則有 h <= 2bh(root)。由于 root 是黑色(性質(zhì)2),所以 bh(root) >= 1。同時(shí),由于每條從 root 到葉子節(jié)點(diǎn)路徑上至少包含 bh(root) 個(gè)黑色節(jié)點(diǎn),所以 n >= 2^bh(root) - 1。綜合起來,有 h <= 2bh(root) <= 2log(n+1)。
  • 性質(zhì)2:在一棵紅黑樹中,從根節(jié)點(diǎn)到葉子節(jié)點(diǎn)的所有路徑中,最長的路徑不超過最短路徑的兩倍。證明:設(shè) h 是紅黑樹的高度,bh 是紅黑樹的黑高。由于最短路徑上全部是黑色節(jié)點(diǎn),所以最短路徑長度為 bh。由于最長路徑上紅色節(jié)點(diǎn)和黑色節(jié)點(diǎn)交替出現(xiàn)(性質(zhì)4),所以最長路徑長度為 2bh。因此,最長路徑長度不超過最短路徑長度的兩倍。
  • 性質(zhì)3:對(duì)于一棵有 n 個(gè)內(nèi)部節(jié)點(diǎn)的紅黑樹,其黑高 bh 滿足不等式 bh >= log(n+1)/2。證明:由性質(zhì)1可得 h <= 2log(n+1),又由于 h >= bh,所以 bh <= log(n+1)/2。紅黑樹的操作

紅黑樹的操作

紅黑樹的基本操作包括查找、插入和刪除。查找操作和普通的二叉查找樹一樣,不需要特殊處理。插入和刪除操作則需要考慮如何維護(hù)紅黑性質(zhì),避免樹失去平衡。為了實(shí)現(xiàn)這一目的,我們需要使用兩種基本操作:顏色變換和旋轉(zhuǎn)。

顏色變換

顏色變換是指將某個(gè)節(jié)點(diǎn)的顏色從紅色變?yōu)楹谏蛘邚暮谏優(yōu)榧t色。這個(gè)操作很簡(jiǎn)單,只需要修改節(jié)點(diǎn)的顏色屬性即可。但是,顏色變換會(huì)影響紅黑性質(zhì),尤其是性質(zhì)4和性質(zhì)5。因此,在進(jìn)行顏色變換時(shí),需要注意以下幾點(diǎn):

  • 顏色變換不能作用于根節(jié)點(diǎn),否則會(huì)違反性質(zhì)2(根是黑色)。
  • 顏色變換不能作用于 NIL 節(jié)點(diǎn),否則會(huì)違反性質(zhì)3(所有葉子都是黑色)。
  • 顏色變換不能使一個(gè)節(jié)點(diǎn)從紅色變?yōu)榧t色,或者從黑色變?yōu)楹谏駝t沒有意義。
  • 顏色變換不能使一個(gè)節(jié)點(diǎn)和其父節(jié)點(diǎn)或子節(jié)點(diǎn)同為紅色,否則會(huì)違反性質(zhì)4(每個(gè)紅色節(jié)點(diǎn)必須有兩個(gè)黑色的子節(jié)點(diǎn))。

旋轉(zhuǎn)

旋轉(zhuǎn)是指將某個(gè)節(jié)點(diǎn)沿著其父子關(guān)系向上或向下移動(dòng)的操作。旋轉(zhuǎn)分為左旋和右旋兩種,左旋是將某個(gè)節(jié)點(diǎn)向上提升為其右孩子的父親,右旋是將某個(gè)節(jié)點(diǎn)向下降低為其左孩子的孩子。旋轉(zhuǎn)操作可以保持二叉查找樹的性質(zhì)不變,但會(huì)改變樹的形狀和高度。旋轉(zhuǎn)操作可以用來調(diào)整樹的平衡狀態(tài),使之更加接近理想的情況。

下圖展示了左旋和右旋的示意圖:

左旋左旋

右旋右旋

左旋操作可以描述如下:

  • 設(shè) x 是要進(jìn)行左旋的節(jié)點(diǎn),y 是 x 的右孩子。
  • 將 y 的左孩子設(shè)為 x 的右孩子,并將 y 的左孩子的父親設(shè)為 x。
  • 將 x 的父親設(shè)為 y 的父親,并更新 y 的父親的相應(yīng)孩子指針。
  • 將 y 的左孩子設(shè)為 x,并將 x 的父親設(shè)為 y。

右旋操作與左旋類似。

紅黑樹的插入

紅黑樹的插入操作是在二叉查找樹的基礎(chǔ)上進(jìn)行的,即先按照二叉查找樹的規(guī)則找到合適的位置插入新節(jié)點(diǎn),然后再調(diào)整樹的結(jié)構(gòu)和顏色,使之恢復(fù)紅黑性質(zhì)。插入操作的具體步驟如下:

  • 創(chuàng)建一個(gè)新節(jié)點(diǎn) z,并將其顏色設(shè)為紅色。
  • 按照二叉查找樹的規(guī)則,將 z 插入到合適的位置,并將其父節(jié)點(diǎn)設(shè)為 y。
  • 如果 y 是 NIL 或者黑色,那么不需要做任何調(diào)整,直接返回。

  • 如果 y 是紅色,那么就存在雙紅問題,即 z 和 y 都是紅色,違反了性質(zhì)4。此時(shí),需要根據(jù) y 的叔叔節(jié)點(diǎn)(y 的父節(jié)點(diǎn)的兄弟節(jié)點(diǎn))的顏色進(jìn)行不同的處理:如果 y 的叔叔節(jié)點(diǎn)是紅色,那么將 y 和其叔叔節(jié)點(diǎn)的顏色都變?yōu)楹谏?,?y 的父節(jié)點(diǎn)的顏色變?yōu)榧t色,并將 y 的父節(jié)點(diǎn)作為新的 z 節(jié)點(diǎn),繼續(xù)向上調(diào)整。如果 y 的叔叔節(jié)點(diǎn)是黑色或 NIL,那么需要進(jìn)行旋轉(zhuǎn)操作。具體來說,分為以下四種情況:如果 z 是 y 的右孩子,并且 y 是其父節(jié)點(diǎn)的左孩子,那么對(duì) y 進(jìn)行左旋,然后交換 z 和 y 的角色。如果 z 是 y 的左孩子,并且 y 是其父節(jié)點(diǎn)的右孩子,那么對(duì) y 進(jìn)行右旋,然后交換 z 和 y 的角色。如果 z 是 y 的左孩子,并且 y 是其父節(jié)點(diǎn)的左孩子,那么對(duì) y 的父節(jié)點(diǎn)進(jìn)行右旋,然后將 y 的顏色變?yōu)楹谏?,?y 的父節(jié)點(diǎn)(原來的祖父節(jié)點(diǎn))的顏色變?yōu)榧t色,并結(jié)束調(diào)整。如果 z 是 y 的右孩子,并且 y 是其父節(jié)點(diǎn)的右孩子,那么對(duì) y 的父節(jié)點(diǎn)進(jìn)行左旋,然后將 y 的顏色變?yōu)楹谏?,?y 的父節(jié)點(diǎn)(原來的祖父節(jié)點(diǎn))的顏色變?yōu)榧t色,并結(jié)束調(diào)整。

下圖展示了插入操作的示意圖:

紅黑樹的刪除

紅黑樹的刪除操作也是在二叉查找樹的基礎(chǔ)上進(jìn)行的,即先按照二叉查找樹的規(guī)則找到要?jiǎng)h除的節(jié)點(diǎn),并用其后繼節(jié)點(diǎn)或前驅(qū)節(jié)點(diǎn)替換它(如果存在),然后再調(diào)整樹的結(jié)構(gòu)和顏色,使之恢復(fù)紅黑性質(zhì)。刪除操作的具體步驟如下:

  • 找到要?jiǎng)h除的節(jié)點(diǎn) z,并用其后繼節(jié)點(diǎn)或前驅(qū)節(jié)點(diǎn)(如果存在)替換它。設(shè)替換后的新節(jié)點(diǎn)為 x。
  • 如果 x 和 z 中至少有一個(gè)是紅色,那么不需要做任何調(diào)整,直接返回。
  • 如果 x 和 z 都是黑色,那么就存在過度黑問題,即 x 多了一個(gè)額外的黑色屬性(因?yàn)樘鎿Q了 z),導(dǎo)致違反了性質(zhì)5。此時(shí),需要根據(jù) x 的兄弟節(jié)點(diǎn)(x 的父節(jié)點(diǎn)的另一個(gè)孩子)的顏色進(jìn)行不同的處理:如果 x 的兄弟節(jié)點(diǎn)是紅色,那么將 x 的兄弟節(jié)點(diǎn)的顏色變?yōu)楹谏瑢?x 的父節(jié)點(diǎn)的顏色變?yōu)榧t色,并對(duì) x 的父節(jié)點(diǎn)進(jìn)行左旋(如果 x 是左孩子)或右旋(如果 x 是右孩子),然后更新 x 的兄弟節(jié)點(diǎn)。如果 x 的兄弟節(jié)點(diǎn)是黑色,那么分為以下四種情況:如果 x 的兄弟節(jié)點(diǎn)的兩個(gè)孩子都是黑色或 NIL,那么將 x 的兄弟節(jié)點(diǎn)的顏色變?yōu)榧t色,并將 x 的父節(jié)點(diǎn)作為新的 x 節(jié)點(diǎn),繼續(xù)向上調(diào)整。如果 x 是左孩子,并且 x 的兄弟節(jié)點(diǎn)的左孩子是紅色,右孩子是黑色或 NIL,那么將 x 的兄弟節(jié)點(diǎn)的顏色變?yōu)榧t色,將 x 的兄弟節(jié)點(diǎn)的左孩子的顏色變?yōu)楹谏?,并?duì) x 的兄弟節(jié)點(diǎn)進(jìn)行右旋,然后更新 x 的兄弟節(jié)點(diǎn)。如果 x 是右孩子,并且 x 的兄弟節(jié)點(diǎn)的右孩子是紅色,左孩子是黑色或 NIL,那么將 x 的兄弟節(jié)點(diǎn)的顏色變?yōu)榧t色,將 x 的兄弟節(jié)點(diǎn)的右孩子的顏色變?yōu)楹谏?,并?duì) x 的兄弟節(jié)點(diǎn)進(jìn)行左旋,然后更新 x 的兄弟節(jié)點(diǎn)。如果 x 是左孩子,并且 x 的兄弟節(jié)點(diǎn)的右孩子是紅色,那么將 x 的父節(jié)點(diǎn)的顏色賦給 x 的兄弟節(jié)點(diǎn),將 x 的父節(jié)點(diǎn)和 x 的兄弟節(jié)點(diǎn)的右孩子的顏色都變?yōu)楹谏?duì) x 的父節(jié)點(diǎn)進(jìn)行左旋,然后結(jié)束調(diào)整。如果 x 是右孩子,并且 x 的兄弟節(jié)點(diǎn)的左孩子是紅色,那么將 x 的父節(jié)點(diǎn)的顏色賦給 x 的兄弟節(jié)點(diǎn),將 x 的父節(jié)點(diǎn)和 x 的兄弟節(jié)點(diǎn)的左孩子的顏色都變?yōu)楹谏?,并?duì) x 的父節(jié)點(diǎn)進(jìn)行右旋,然后結(jié)束調(diào)整。

紅黑樹的實(shí)現(xiàn)

下面給出了用 java語言實(shí)現(xiàn)紅黑樹的部分代碼。首先定義了一個(gè) Node 類,表示樹中的每個(gè)節(jié)點(diǎn)。每個(gè)節(jié)點(diǎn)有五個(gè)屬性:key(鍵值),color(顏色),left(左孩子),right(右孩子)和 parent(父親)。其中 color 用 0 表示黑色,用 1 表示紅色。NIL 節(jié)點(diǎn)用 None 表示。

// 定義一個(gè) Node 類,表示樹中的每個(gè)節(jié)點(diǎn)
class Node {
    int key; // 鍵值
    int color; // 顏色,0 表示黑色,1 表示紅色
    Node left; // 左孩子
    Node right; // 右孩子
    Node parent; // 父親

    // 構(gòu)造方法,初始化鍵值和顏色
    public Node(int key, int color) {
        this.key = key;
        this.color = color;
        this.left = null;
        this.right = null;
        this.parent = null;
    }
}

// 定義一個(gè) RBTree 類,表示一棵紅黑樹
class RBTree {
    Node root; // 根節(jié)點(diǎn)

    // 構(gòu)造方法,初始化根節(jié)點(diǎn)為 null
    public RBTree() {
        this.root = null;
    }

    // 輔助方法,對(duì)某個(gè)節(jié)點(diǎn)進(jìn)行左旋操作
    public void leftRotate(Node x) {
        // 設(shè) y 是 x 的右孩子
        Node y = x.right;
        // 將 y 的左孩子設(shè)為 x 的右孩子,并將 y 的左孩子的父親設(shè)為 x
        x.right = y.left;
        if (y.left != null) {
            y.left.parent = x;
        }
        // 將 x 的父親設(shè)為 y 的父親,并更新 y 的父親的相應(yīng)孩子指針
        y.parent = x.parent;
        if (x.parent == null) { // 如果 x 是根節(jié)點(diǎn),那么將 y 設(shè)為新的根節(jié)點(diǎn)
            this.root = y;
        } else if (x == x.parent.left) { // 如果 x 是其父節(jié)點(diǎn)的左孩子,那么將 y 設(shè)為其父節(jié)點(diǎn)的左孩子
            x.parent.left = y;
        } else { // 如果 x 是其父節(jié)點(diǎn)的右孩子,那么將 y 設(shè)為其父節(jié)點(diǎn)的右孩子
            x.parent.right = y;
        }
        // 將 y 的左孩子設(shè)為 x,并將 x 的父親設(shè)為 y
        y.left = x;
        x.parent = y;
    }

    // 輔助方法,對(duì)某個(gè)節(jié)點(diǎn)進(jìn)行右旋操作
    public void rightRotate(Node x) {
        // 設(shè) y 是 x 的左孩子
        Node y = x.left;
        // 將 y 的右孩子設(shè)為 x 的左孩子,并將 y 的右孩子的父親設(shè)為 x
        x.left = y.right;
        if (y.right != null) {
            y.right.parent = x;
        }
        // 將 x 的父親設(shè)為 y 的父親,并更新 y 的父親的相應(yīng)孩子指針
        y.parent = x.parent;
        if (x.parent == null) { // 如果 x 是根節(jié)點(diǎn),那么將 y 設(shè)為新的根節(jié)點(diǎn)
            this.root = y;
        } else if (x == x.parent.right) { // 如果 x 是其父節(jié)點(diǎn)的右孩子,那么將 y 設(shè)為其父節(jié)點(diǎn)的右孩子
            x.parent.right = y;
        } else { // 如果 x 是其父節(jié)點(diǎn)的左孩子,那么將 y 設(shè)為其父節(jié)點(diǎn)的左孩子
            x.parent.left = y;
        }
        // 將 y 的右孩子設(shè)為 x,并將 x 的父親設(shè)為 y
        y.right = x;
        x.parent = y;
    }

    // 輔助方法,用 v 節(jié)點(diǎn)替換 u 節(jié)點(diǎn)
    public void transplant(Node u, Node v) {
        if (u.parent == null) { // 如果 u 是根節(jié)點(diǎn),那么將 v 設(shè)為新的根節(jié)點(diǎn)
            this.root = v;
        } else if (u == u.parent.left) { // 如果 u 是其父節(jié)點(diǎn)的左孩子,那么將 v 設(shè)為其父節(jié)點(diǎn)的左孩子
            u.parent.left = v;
        } else { // 如果 u 是其父節(jié)點(diǎn)的右孩子,那么將 v 設(shè)為其父節(jié)點(diǎn)的右孩子
            u.parent.right = v;
        }
        if (v != null) { // 如果 v 不是 null,那么將 v 的父親設(shè)為 u 的父親
            v.parent = u.parent;
        }
    }

    // 輔助方法,返回以某個(gè)節(jié)點(diǎn)為根的子樹中最小鍵值的節(jié)點(diǎn)
    public Node minimum(Node x) {
        while (x.left != null) { // 沿著左孩子指針一直向下,直到找到最左邊的節(jié)點(diǎn)
            x = x.left;
        }
        return x;
    }

    // 輔助方法,返回以某個(gè)節(jié)點(diǎn)為根的子樹中最大鍵值的節(jié)點(diǎn)
    public Node maximum(Node x) {
        while (x.right != null) { // 沿著右孩子指針一直向下,直到找到最右邊的節(jié)點(diǎn)
            x = x.right;
        }
        return x;
    }

    // 輔助方法,返回樹中鍵值等于 key 的第一個(gè)找到的節(jié)點(diǎn)
    public Node search(int key) {
        Node x = this.root; // 從根節(jié)點(diǎn)開始查找
        while (x != null && x.key != key) { // 如果 x 不是 null,并且 x 的鍵值不等于 key,那么繼續(xù)查找
            if (key < x.key) { // 如果 key 小于 x 的鍵值,那么在 x 的左子樹中查找
                x = x.left;
            } else { // 如果 key 大于 x 的鍵值,那么在 x 的右子樹中查找
                x = x.right;
            }
        }
        return x; // 返回找到的節(jié)點(diǎn)或 null
    }

    // 向樹中插入一個(gè)鍵值為 key 的節(jié)點(diǎn)
    public void insert(int key) {
        Node z = new Node(key, 1); // 創(chuàng)建一個(gè)新節(jié)點(diǎn) z,并將其顏色設(shè)為紅色
        Node y = null; // 初始化 y 為 null,用于記錄 z 的父節(jié)點(diǎn)
        Node x = this.root; // 初始化 x 為根節(jié)點(diǎn),用于查找 z 的插入位置
        while (x != null) { // 如果 x 不是 null,那么繼續(xù)查找
            y = x; // 將 y 設(shè)為當(dāng)前的 x 節(jié)點(diǎn)
            if (z.key < x.key) { // 如果 z 的鍵值小于 x 的鍵值,那么在 x 的左子樹中查找
                x = x.left;
            } else { // 如果 z 的鍵值大于或等于 x 的鍵值,那么在 x 的右子樹中查找
                x = x.right;
            }
        }
        z.parent = y; // 將 z 的父節(jié)點(diǎn)設(shè)為 y
        if (y == null) { // 如果 y 是 null,說明樹是空的,那么將 z 設(shè)為根節(jié)點(diǎn)
            this.root = z;
        } else if (z.key < y.key) { // 如果 z 的鍵值小于 y 的鍵值,那么將 z 設(shè)為 y 的左孩子
            y.left = z;
        } else { // 如果 z 的鍵值大于或等于 y 的鍵值,那么將 z 設(shè)為 y 的右孩子
            y.right = z;
        }
        insertFixup(z); // 調(diào)用插入修復(fù)方法,恢復(fù)紅黑性質(zhì)
    }

   // 插入修復(fù)方法,用于恢復(fù)紅黑樹的性質(zhì)
public void insertFixup(Node z) {
    // 當(dāng) z 的父節(jié)點(diǎn)存在且是紅色時(shí),需要進(jìn)行調(diào)整
    while (z.parent != null && z.parent.color == Color.RED) {
        // 判斷 z 的父節(jié)點(diǎn)是其祖父節(jié)點(diǎn)的左孩子還是右孩子
        if (z.parent == z.parent.parent.left) {
            // 如果是左孩子,那么獲取 z 的叔叔節(jié)點(diǎn)(祖父節(jié)點(diǎn)的右孩子)
            Node y = z.parent.parent.right;
            // 根據(jù)叔叔節(jié)點(diǎn)的顏色分為三種情況
            if (y != null && y.color == Color.RED) {
                // 如果叔叔節(jié)點(diǎn)是紅色,那么將父節(jié)點(diǎn)和叔叔節(jié)點(diǎn)都變?yōu)楹谏?,將祖父?jié)點(diǎn)變?yōu)榧t色,并將祖父節(jié)點(diǎn)作為新的 z 節(jié)點(diǎn),繼續(xù)向上調(diào)整
                z.parent.color = Color.BLACK;
                y.color = Color.BLACK;
                z.parent.parent.color = Color.RED;
                z = z.parent.parent;
            } else {
                // 如果叔叔節(jié)點(diǎn)是黑色或 NIL,那么需要進(jìn)行旋轉(zhuǎn)操作
                if (z == z.parent.right) {
                    // 如果 z 是其父節(jié)點(diǎn)的右孩子,那么先對(duì)父節(jié)點(diǎn)進(jìn)行左旋,然后交換 z 和其父節(jié)點(diǎn)的角色
                    z = z.parent;
                    leftRotate(z);
                }
                // 如果 z 是其父節(jié)點(diǎn)的左孩子,那么對(duì)祖父節(jié)點(diǎn)進(jìn)行右旋,然后將父節(jié)點(diǎn)變?yōu)楹谏?,將祖父?jié)點(diǎn)變?yōu)榧t色,并結(jié)束調(diào)整
                z.parent.color = Color.BLACK;
                z.parent.parent.color = Color.RED;
                rightRotate(z.parent.parent);
            }
        } else {
            // 如果是右孩子,那么獲取 z 的叔叔節(jié)點(diǎn)(祖父節(jié)點(diǎn)的左孩子),與上面類似,只是左右對(duì)稱
            Node y = z.parent.parent.left;
            // 根據(jù)叔叔節(jié)點(diǎn)的顏色分為三種情況
            if (y != null && y.color == Color.RED) {
                // 如果叔叔節(jié)點(diǎn)是紅色,那么將父節(jié)點(diǎn)和叔叔節(jié)點(diǎn)都變?yōu)楹谏?,將祖父?jié)點(diǎn)變?yōu)榧t色,并將祖父節(jié)點(diǎn)作為新的 z 節(jié)點(diǎn),繼續(xù)向上調(diào)整
                z.parent.color = Color.BLACK;
                y.color = Color.BLACK;
                z.parent.parent.color = Color.RED;
                z = z.parent.parent;
            } else {
                // 如果叔叔節(jié)點(diǎn)是黑色或 NIL,那么需要進(jìn)行旋轉(zhuǎn)操作
                if (z == z.parent.left) {
                    // 如果 z 是其父節(jié)點(diǎn)的左孩子,那么先對(duì)父節(jié)點(diǎn)進(jìn)行右旋,然后交換 z 和其父節(jié)點(diǎn)的角色
                    z = z.parent;
                    rightRotate(z);
                }
                // 如果 z 是其父節(jié)點(diǎn)的右孩子,那么對(duì)祖父節(jié)點(diǎn)進(jìn)行左旋,然后將父節(jié)點(diǎn)變?yōu)楹谏瑢⒆娓腹?jié)點(diǎn)變?yōu)榧t色,并結(jié)束調(diào)整
                z.parent.color = Color.BLACK;
                z.parent.parent.color = Color.RED;
                leftRotate(z.parent.parent);
            }
        }
    }
    // 最后確保根節(jié)點(diǎn)是黑色
    this.root.color = Color.BLACK;
}

// 從樹中刪除一個(gè)鍵值為 key 的節(jié)點(diǎn)
public void delete(int key) {
    Node z = search(key); // 查找要?jiǎng)h除的節(jié)點(diǎn)
    if (z == null) { // 如果沒有找到,直接返回
        return;
    }
    Node x; // 用于記錄替換后的新節(jié)點(diǎn)
    Node y = z; // 用于記錄要?jiǎng)h除或移動(dòng)的原始節(jié)點(diǎn)
    Color yOriginalColor = y.color; // 用于記錄 y 的原始顏色
    if (z.left == null) { // 如果 z 沒有左孩子,那么用其右孩子替換它
        x = z.right;
        transplant(z, z.right);
    } else if (z.right == null) { // 如果 z 沒有右孩子,那么用其左孩子替換它
        x = z.left;
        transplant(z, z.left);
    } else { // 如果 z 有兩個(gè)孩子,那么用其后繼節(jié)點(diǎn)(右子樹中最小的節(jié)點(diǎn))替換它
        y = minimum(z.right); // 找到 z 的后繼節(jié)點(diǎn)
        yOriginalColor = y.color; // 記錄 y 的原始顏色
        x = y.right; // 記錄 y 的右孩子
        if (y.parent == z) { // 如果 y 是 z 的右孩子,那么直接將 x 的父親設(shè)為 y
            x.parent = y;
        } else { // 如果 y 不是 z 的右孩子,那么用 x 替換 y,并將 y 的右孩子設(shè)為 z 的右孩子
            transplant(y, y.right);
            y.right = z.right;
            y.right.parent = y;
        }
        transplant(z, y); // 用 y 替換 z,并將 y 的左孩子設(shè)為 z 的左孩子
        y.left = z.left;
        y.left.parent = y;
        y.color = z.color; // 將 y 的顏色設(shè)為 z 的顏色
    }
    if (yOriginalColor == Color.BLACK) { // 如果 y 的原始顏色是黑色,那么就存在過度黑問題,需要調(diào)用刪除修復(fù)方法,恢復(fù)紅黑性質(zhì)
        deleteFixup(x);
    }
}
  
  // 刪除修復(fù)方法,用于恢復(fù)紅黑性質(zhì)
public void deleteFixup(Node x) {
    // 當(dāng) x 不是根節(jié)點(diǎn),并且是黑色時(shí),需要進(jìn)行調(diào)整
    while (x != this.root && x.color == Color.BLACK) {
        // 判斷 x 是其父節(jié)點(diǎn)的左孩子還是右孩子
        if (x == x.parent.left) {
            // 如果是左孩子,那么獲取 x 的兄弟節(jié)點(diǎn)(父節(jié)點(diǎn)的右孩子)
            Node w = x.parent.right;
            // 根據(jù)兄弟節(jié)點(diǎn)的顏色分為四種情況
            if (w.color == Color.RED) {
                // 如果兄弟節(jié)點(diǎn)是紅色,那么將兄弟節(jié)點(diǎn)變?yōu)楹谏?,將父?jié)點(diǎn)變?yōu)榧t色,并對(duì)父節(jié)點(diǎn)進(jìn)行左旋,然后更新兄弟節(jié)點(diǎn)
                w.color = Color.BLACK;
                x.parent.color = Color.RED;
                leftRotate(x.parent);
                w = x.parent.right;
            }
            if (w.left.color == Color.BLACK && w.right.color == Color.BLACK) {
                // 如果兄弟節(jié)點(diǎn)的兩個(gè)孩子都是黑色或 NIL,那么將兄弟節(jié)點(diǎn)變?yōu)榧t色,并將父節(jié)點(diǎn)作為新的 x 節(jié)點(diǎn),繼續(xù)向上調(diào)整
                w.color = Color.RED;
                x = x.parent;
            } else {
                // 如果兄弟節(jié)點(diǎn)的兩個(gè)孩子不都是黑色或 NIL,那么需要進(jìn)行旋轉(zhuǎn)操作
                if (w.right.color == Color.BLACK) {
                    // 如果兄弟節(jié)點(diǎn)的右孩子是黑色或 NIL,那么將兄弟節(jié)點(diǎn)變?yōu)榧t色,將兄弟節(jié)點(diǎn)的左孩子變?yōu)楹谏?,并?duì)兄弟節(jié)點(diǎn)進(jìn)行右旋,然后更新兄弟節(jié)點(diǎn)
                    w.color = Color.RED;
                    w.left.color = Color.BLACK;
                    rightRotate(w);
                    w = x.parent.right;
                }
                // 如果兄弟節(jié)點(diǎn)的右孩子是紅色,那么將父節(jié)點(diǎn)的顏色賦給兄弟節(jié)點(diǎn),將父節(jié)點(diǎn)和兄弟節(jié)點(diǎn)的右孩子都變?yōu)楹谏?duì)父節(jié)點(diǎn)進(jìn)行左旋,然后結(jié)束調(diào)整
                w.color = x.parent.color;
                x.parent.color = Color.BLACK;
                w.right.color = Color.BLACK;
                leftRotate(x.parent);
                x = this.root; // 將 x 設(shè)為根節(jié)點(diǎn),結(jié)束循環(huán)
            }
        } else {
            // 如果是右孩子,那么獲取 x 的兄弟節(jié)點(diǎn)(父節(jié)點(diǎn)的左孩子),與上面類似,只是左右對(duì)稱
            Node w = x.parent.left;
            // 根據(jù)兄弟節(jié)點(diǎn)的顏色分為四種情況
            if (w.color == Color.RED) {
                // 如果兄弟節(jié)點(diǎn)是紅色,那么將兄弟節(jié)點(diǎn)變?yōu)楹谏?,將父?jié)點(diǎn)變?yōu)榧t色,并對(duì)父節(jié)點(diǎn)進(jìn)行右旋,然后更新兄弟節(jié)點(diǎn)
                w.color = Color.BLACK;
                x.parent.color = Color.RED;
                rightRotate(x.parent);
                w = x.parent.left;
            }
            if (w.right.color == Color.BLACK && w.left.color == Color.BLACK) {
                // 如果兄弟節(jié)點(diǎn)的兩個(gè)孩子都是黑色或 NIL,那么將兄弟節(jié)點(diǎn)變?yōu)榧t色,并將父節(jié)點(diǎn)作為新的 x 節(jié)點(diǎn),繼續(xù)向上調(diào)整
                w.color = Color.RED;
                x = x.parent;
            } else {
                // 如果兄弟節(jié)點(diǎn)的兩個(gè)孩子不都是黑色或 NIL,那么需要進(jìn)行旋轉(zhuǎn)操作
                if (w.left.color == Color.BLACK) {
                    // 如果兄弟節(jié)點(diǎn)的左孩子是黑色或 NIL,那么將兄弟節(jié)點(diǎn)變?yōu)榧t色,將兄弟節(jié)點(diǎn)的右孩子變?yōu)楹谏?,并?duì)兄弟節(jié)點(diǎn)進(jìn)行左旋,然后更新兄弟節(jié)點(diǎn)
                    w.color = Color.RED;
                    w.right.color = Color.BLACK;
                    leftRotate(w);
                    w = x.parent.left;
                }
                // 如果兄弟節(jié)點(diǎn)的左孩子是紅色,那么將父節(jié)點(diǎn)的顏色賦給兄弟節(jié)點(diǎn),將父節(jié)點(diǎn)和兄弟節(jié)點(diǎn)的左孩子都變?yōu)楹谏?,并?duì)父節(jié)點(diǎn)進(jìn)行右旋,然后結(jié)束調(diào)整
                w.color = x.parent.color;
                x.parent.color = Color.BLACK;
                w.left.color = Color.BLACK;
                rightRotate(x.parent);
                x = this.root; // 將 x 設(shè)為根節(jié)點(diǎn),結(jié)束循環(huán)
            }
        }
    }
    // 最后確保根節(jié)點(diǎn)是黑色
    this.root.color = Color.BLACK;
}


責(zé)任編輯:華軒 來源: 今日頭條
相關(guān)推薦

2021-08-29 07:41:48

數(shù)據(jù)HashMap底層

2023-03-31 08:24:29

數(shù)據(jù)結(jié)構(gòu)算法數(shù)目

2024-11-07 15:36:34

2021-08-31 07:36:22

LinkedListAndroid數(shù)據(jù)結(jié)構(gòu)

2016-12-08 11:01:39

紅黑樹Java

2016-10-09 08:57:11

python數(shù)據(jù)結(jié)構(gòu)與算法樹形結(jié)構(gòu)

2017-09-06 10:55:19

Java

2020-06-09 08:13:15

PHP數(shù)據(jù)結(jié)構(gòu)

2021-01-07 08:12:47

數(shù)據(jù)結(jié)構(gòu)二叉樹

2020-09-17 07:37:09

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

2022-06-10 08:17:52

HashMap鏈表紅黑樹

2020-05-06 16:41:36

紅黑樹二叉查找樹

2023-03-28 07:59:57

ReactReconciler

2023-09-13 08:08:41

Redis消息隊(duì)列

2019-06-21 15:20:05

Redis數(shù)據(jù)結(jié)構(gòu)數(shù)據(jù)庫

2020-03-20 10:47:51

Redis數(shù)據(jù)庫字符串

2020-03-11 08:40:51

紅黑樹平衡二叉B樹

2019-08-22 09:22:44

數(shù)據(jù)結(jié)構(gòu)二叉搜索樹

2019-10-12 08:36:48

Java程序員數(shù)據(jù)結(jié)構(gòu)

2023-01-04 07:54:03

HashMap底層JDK
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)