數據結構之紅黑樹

這是我參與8月更文挑戰的第13天,活動詳情查看:8月更文挑戰html

紅黑樹的定義

紅黑樹是一棵二叉搜索樹,其在每一個結點上增長了一個存儲位用於表示顏色。經過對根到葉子的每條路徑上的顏色的管理,實現了近似平衡(紅黑樹確保一條路徑不會比另一條路徑高出兩倍),其能夠保證在最壞條件下動態集合操做的時間複雜度爲 O ( l g N ) O(lgN) .markdown

image.png

二叉搜索樹

先簡單介紹下二叉搜索樹:二叉搜索樹可使用一個鏈表來進行表示,其中每一個結點就是一個對象。除了key等一些數據外,其還包括屬性leftright、和p,分別指向左子結點右子結點雙親。若是不存在則指向NIL數據結構

二叉搜索樹知足對於其任意結點X,左子樹的關鍵字最大不能超過X.key而右子樹不能小於X.key,大部分搜索樹操做的最壞時間都和樹高成正比app

image.png

紅黑樹的性質

介紹完二叉搜索樹,接下來咱們來介紹下紅黑樹知足的幾個性質:post

  1. 每一個節點要麼黑色要麼紅色
  2. 根節點必須是黑色
  3. 每一個葉子節點(葉子結點是NIL結點)是黑色的。(一個結點沒有子結點或者父結點,該結點對應相應的指針就指向NIL)
  4. 每一個紅色結點的兩個子結點都是黑色的
  5. 從任一結點到其每一個葉子的全部路徑都包含相同數目的黑結點

根據性質4咱們能夠獲得,從根到葉節點的簡單路徑上,至少有一半的結點爲黑結點。spa

紅黑樹不是完美平衡的二叉查找樹,可是其任意一個結點到每一個葉子結點的路徑都包含相同數量的黑色結點。因此咱們叫紅黑樹的這種平衡爲黑色完美平衡指針

紅黑樹維持平衡的方式

紅黑樹爲了保持平衡有三種操做:code

左旋:以某個結點做爲支點(旋轉結點),其右子結點變爲旋轉結點的父節點.右子結點的左子結點變成旋轉結點的右子結點,左子結點保持不變。orm

右旋:以某個結點做爲支點(旋轉結點),其左子結點變爲旋轉結點的父節點.左子結點的右子結點變成旋轉結點的左子結點,右子結點保持不變。xml

變色:結點顏色由紅色變成黑色或者由黑色變成紅色

紅黑樹就是經過旋轉和變色實現自平衡的。

image.png

從下圖中能夠發現,以11結點爲旋轉結點,其右子結點18變成父結點,而後結點18的左子結點14變成11結點的右子結點。

image.png

紅黑樹的查找

紅黑樹就是一棵二叉搜索樹,因此其查找結點的方式和普通二叉搜索樹同樣,可是由於紅黑樹平衡性比較好,因此它查找的最壞時間複雜度爲 O ( 2 l g N ) O(2lgN)

紅黑樹的插入

紅黑樹的插入時間複雜度爲 O ( l g n ) O(lgn) ,紅黑樹的插入和二叉搜索樹的插入相似,不過紅黑樹會判斷相應結點顏色,並給該結點上色。下面上一段僞代碼:

RB-INSERT(T,z)  // T爲紅黑樹、z爲要插入的結點
// y設爲nil結點
y = T.nil
// x設爲根節點
x = T.root
while x != T.nil // x不爲空
    y = x 
    if z.key< x.key  // 插入的結點比該樹小
        x = x.left
    else 
        x = x.right
z.p = y  // 設置插入結點的父結點爲 y
if y == T.nil  // 該樹爲空
    T.root = z
else if z.key < y.key
    y.left = z
else
    y.right = z
// 將插入的結點的左右子結點設爲nil
z.left = T.nil
z.right = T.nil
z.color = RED
RB-INSERT-FIXUP(T,z)  // 調整着色
複製代碼
while z.p.color == RED
    if z.p == z.p.p.left  // 判斷父結點是不是祖父結點的左結點
        y = z.p.p.right  // y設爲祖父結點的右節點
        if y.color = RED  // 判斷y的顏色 
            z.p.color = BLACK  // 將z的父結點的顏色設爲黑色 (狀況1)
            y.color = BLACK  // 將z的叔父結點的顏色設爲黑色 (狀況1)
            z.p.p.color = RED  // 將z的祖父結點的顏色設爲紅色 (狀況1)
            z = z.p.p  // 將z結點設爲其祖父結點 (狀況1)
        else if z == z.p.right  // 若是z結點是父結點的右子結點
            z = z.p  // 將z結點設爲z的父結點 (狀況2)
            // 對結點進行左旋 
            LETF-ROTATE(T,z)  (狀況2)
        z.p.color = BLACK  // 將z的父結點顏色設爲黑色 (狀況3)
        z.p.p.color = RED  // 將z的祖父結點的顏色設爲紅色 (狀況3)
        // 對結點進行右旋
        RIGHT-ROTATE(T,z.p.p)  (狀況3else  // 與上面的狀況相似
        y = z.p.p.left   
        if y.color = RED
            z.p.color = BLACK
            y.color = BLACK
            z.p.p.color = RED
            z = z.p.p
        else if z == z.p.left
            z = z.p
            // 對結點進行左旋
            RIGHT-ROTATE(T,z)
        z.p.color = BLACK
        z.p.p.color = RED
        // 對結點進行右旋
        LEFT-ROTATE(T,z.p.p)
T.root.color = BLACK
複製代碼

具體左旋和右旋的代碼在這我就不貼出了,若是想了解本身能夠去網上了解一下。

以下圖:具體能夠對照代碼進行了解,開始先是判斷該結點的父結以及叔父結點的顏色,進行判斷插入結點的顏色是否知足,不知足則進行調整,調整完顏色再進行相應的左旋和右旋。

image.png

紅黑樹的刪除

雖然紅黑樹的刪除比插入還要複雜一點,可是爲了節省篇幅就再也不貼代碼了。紅黑樹的刪除操做須要的時間複雜度也是 O ( l g N ) O(lgN)

  • 無子節點時,刪除節點可能爲紅色或者黑色;
    1.1 若是爲紅色,直接刪除便可,不會影響黑色節點的數量;
    1.2 若是爲黑色,則須要進行刪除平衡的操做了;
  • 只有一個子節點時,刪除節點只能是黑色,其子節點爲紅色,不然沒法知足紅黑樹的性質了。 此時用刪除節點的子節點接到父節點,且將子節點顏色塗黑,保證黑色數量。
  • 有兩個子節點時,與二叉搜索樹同樣,使用後繼節點做爲替換的刪除節點,情形轉至爲1或2處理。

具體步驟就不詳細展開了,以後再寫一篇關於紅黑樹刪除的文章特意進行分析。

結語

第一篇數據結構-堆

該篇是我記錄的數據結構篇的第二篇,因爲本人技術水平有限,可能寫的沒有那麼準確,若有什麼問題,但願你們可以幫我指出,萬分感謝!

相關文章
相關標籤/搜索