1. 程式人生 > 程式設計 >win10安裝python3.6的常見問題

win10安裝python3.6的常見問題

紅黑樹(Red Black Tree)是一種自平衡的二叉搜尋樹(Self-balancing Binary Search Tree)。以前也叫平衡二叉B樹(Symmetric Binary B-Tree)

1、前言

樹的結構

1.1 平衡二叉搜尋樹

平衡二叉搜尋樹(Balanced Binary Search Tree),簡稱BBST,常見的平衡二叉搜尋樹是AVL和紅黑樹

  • 二叉搜尋樹

二叉搜尋樹(Binary Search Tree)是二叉樹的一種,簡稱BST。又稱為二叉查詢樹、二叉排序樹

他的特點是任何一個結點的值都大於其左子樹的所有結點的值,任何一個結點的值都小於其右子樹的所有結點

  • 平衡

平衡(Balance):就是當結點數量固定時,左右子樹的高度越接近,這顆二叉樹越平衡(高度越低)。最理想的平衡就是完全二叉樹/滿二叉樹,高度最小的二叉樹。

一顆二叉搜尋樹平均時間複雜度可以認為是樹的高度O(h)。

像左邊這棵樹,結點左右子樹的高度相差1,屬於平衡二叉搜尋樹,O(h)=O(logn)

右邊的高度達到了最大,已經退化為連結串列,O(h)=O(n)

  • 改進二叉搜尋樹

當二叉樹退化成連結串列時,效能很低,所以我們需要在結點插入、刪除操作之後,想辦法讓二叉搜尋樹恢復平衡(減小樹的高度)

但是如果為了追求最理想的平衡,而增加了時間複雜度也不是很有必要,因此比較合理的方案就是:用盡量少的調整次數達到適度平衡。

由此引申出 AVL 樹的概念。

1.2 AVL樹

AVL樹是最早發明的自平衡二叉搜尋樹之一,它取名字兩位發明家的名字:G.M.Adelson-Velsky 和 E.M.Landis

平衡因子(Balance Factor):某結點的左右子樹的高度差

每個葉子結點的平衡因子都是0、看這棵樹,紅色資料標註了每個結點對應的平衡因子

舉例:

  • 8的左子樹高度為2,右子樹高度為1,因此平衡因子為1
  • 5的左子樹高度0,右子樹高度為3,因此平衡因子為-3
  • 4的左子樹高度為2,右子樹高度4,因此平衡因子為-2

這顆AVL樹和它每個結點對應的平衡因子

可以看出AVL樹具有以下特點:

  1. 每個結點的平衡因子只可能是-1、0、1(如果絕對值超過1,則認為是失衡)
  2. 每個結點的左右子樹高度差不超過1
  3. 搜尋、插入、刪除的時間複雜度為O(logn)

1.3 B樹

B樹(Balanced Tree)是一種平衡的多路搜尋樹,多用於檔案系統、資料庫的實現

一個簡單的3階B樹:

特點:

  1. 1個結點可以儲存超過2個元素,可以擁有超過2個子節點
  2. 擁有二叉搜尋樹的一些特性
  3. 平衡,每個結點的所有子樹高度一致

m階B樹的性質(m>=2)

m階B樹指的是一個結點最多擁有m個子結點。假設一個結點儲存的元素個數為x,那麼如果這個結點是:

  • 根節點:1 ≤ x ≤ m - 1
  • 非根結點:┌ m / 2 ┐ - 1 ≤ x ≤ m - 1

果有子結點,子結點個數為 y = x + 1,那麼如果這個結點是:

  • 根結點:2 ≤ y ≤ m
  • 非根結點:┌ m / 2 ┐ ≤ y ≤ m

向上取整(Ceiling),指的是取比自己大的最小整數,用數學符號 ┌ ┐ 表示;
向下取整(Floor),指的是取比自己小的最大整數,用數學符號 └ ┘ 表示。

比如 m=3,子結點個數 2≤y≤3,這個 B 樹可以稱為(2,3)樹、2-3 樹。
比如 m=4,子結點個數 2≤y≤4,這個 B 樹可以稱為(2,4)樹、2-3-4 樹。比如 m=5,子結點個數 3≤y≤4,這個 B 樹可以稱為(3,5)樹、3-4-5 樹

1.4 B樹VS二叉搜尋樹

這是一棵二叉搜尋樹,通過某些父子結點合併,恰好能與上面的 B 樹對應。我們可以得到結論:

  • B 樹和二叉搜尋樹,在邏輯上是等價的
  • 多代結點合併,可以獲得一個超級結點,且 n 代合併的超級結點,最多擁有 (2^n) 個子結點 (至少是 (2^n) 階 B 樹)

2、紅黑樹的定義和性質

紅黑樹是一種含有紅黑結點並能自平衡的二叉搜尋樹

為了保證平衡,紅黑樹必須滿足一下性質:

  • 每個結點要麼是紅色、要麼是黑色
  • 根節點必須是黑色
  • 葉結點(外部結點、空結點)是黑色
  • 紅色結點不能連續(紅色結點的孩子和父親都是黑色)
  • 對於每個結點,從該點到nil(數尾端,java中為null的結點)的任何路徑都包含所相同個數的黑色結點

3、紅黑樹與B樹的等價變換

對紅黑樹做等價變換,即將所有的紅色結點上升一層與它的父節點放在同一行,這就很像一顆4階B樹,轉換效果如下:

得出結論:

  • 紅黑樹與4階B樹(2-3-4樹)具有等價性
  • 黑色結點與紅色子節點融合在一起,形成1個B樹結點
  • 紅黑樹的黑色結點個數與4階B樹的結點總個數相等

4、紅黑樹的基本操作

當我們對一棵平衡二叉搜尋樹進行插入、刪除的時候,很可能會讓這棵樹變得失衡(最壞可能導致所有祖先結點失衡,但是父結點和非祖先結點都不可能失衡)。

為了達到平衡,需要對樹進行旋轉。而紅黑樹能夠達到自平衡,靠的也就是左旋、右旋和變色。

旋轉操作是區域性的。當一側子樹的結點少了,向另一側“借”一些結點;當一側子樹的結點多了,則“租”一些結點給另一側。

  • N-node:當前結點
  • P-parent:父節點
  • S-sibling:兄弟結點
  • U-uncle:叔叔結點(p的兄弟結點)
  • G-grand:爺爺結點(p的父節點)

4.1 左旋

左旋指的是以某個結點作為支點(旋轉結點),其右子結點變為旋轉結點的父結點,右子結點的左子結點變為旋轉結點的右子結點,左子結點保持不變。

4.2 右旋

右旋指的是以某個結點作為支點(旋轉結點),其左子結點變為旋轉結點的父結點,左子結點的右子結點變為旋轉結點的左子結點,右子結點保持不變。

4.3 變色

變色指的是結點的顏色由紅色變為黑色或由黑色變為紅色

4.4 變換規則

將左旋、右旋和變色結合起來,得到一套變換規則

變色:如果當前結點的父結點和叔叔結點是紅色,那麼:

  • 把父結點和叔叔結點變為黑色
  • 把祖父結點變為紅色
  • 把指標定義到祖父結點

左旋:當前結點是右子樹,且父結點是紅色,叔叔結點是黑色,對它的父結點左旋

右旋:點前結點是左子樹,且父結點是紅色,叔叔結點是黑色,那麼:

  • 把父結點變為黑色
  • 把爺爺結點變為紅色
  • 對爺爺結點右旋

5、紅黑樹搜尋

由於紅黑樹本來就是平衡二叉搜尋樹,並且搜尋也不會破壞樹的平衡,所以搜尋演算法也與平衡二叉樹搜尋一致

具體步驟如下:

  • 從根結點開始檢索,把根結點設定為當前結點。
  • 若當前結點為空,返回 nil。
  • 若當前結點不為空,比較當前結點 key 與搜尋 key 的大小。
  • 若當前結點 key 等於搜尋 key,那麼該 key 就是搜尋目標,返回當前結點。
  • 若當前結點 key 大於搜尋 key,把當前結點的左子結點設定為當前結點,重複步驟 2。
  • 若當前結點 key 小於搜尋 key,把當前結點的右子結點設定為當前結點,重複步驟 2。

6、紅黑樹插入

具體步驟如下:

  • 從根結點開始檢索。
  • 若根結點為空,那麼插入結點設為根結點,結束。
  • 若根結點不為空,那麼把根結點設為當前結點。
  • 若當前結點為 nil,返回當前結點的父結點,結束。
  • 若當前結點 key 等於搜尋 key,那麼該 key 所在結點就是插入結點,更新結點的值,結束。
  • 若當前結點 key 大於搜尋 key,把當前結點的左子結點設定為當前結點,重複步驟 4。
  • 若當前結點 key 小於搜尋 key,把當前結點的右子結點設定為當前結點,重複步驟 4

7、插入後實現自平衡

建議新新增的結點預設為紅色,因此這樣能夠讓紅黑樹的性質儘快滿足。不過如果新增的結點是根結點,設為黑色即可。

紅黑樹插入可能出現的場景:

①場景 1:紅黑樹為空樹
紅黑樹的性質 2:根結點必須是黑色。處理:直接把插入結點設成黑色並作為根結點。②場景 2:插入結點的 key 已存在二叉搜尋樹中不能插入相同元素,既然結點的 key 已經存在,紅黑樹也已平衡,無需重複插入。
處理:

  • 將插入結點設為將要替換結點的顏色
  • 更新當前結點的值為插入結點的值

③場景 3:插入結點的父結點為黑色插入的結點預設是紅色的,當它的父結點是黑色時,並不會破壞平衡。
處理:直接插入。
④場景 4:插入結點的父結點為紅色如果插入結點的父結點為紅色,那麼父結點不可能為根結點,所以插入結點總是存在祖父結點。這點很重要,後續的旋轉操作需要祖父結點的參與。
場景 4.1:存在叔父結點,且為紅色

由紅黑樹性質 4 可知:紅色結點不能連續。那麼此時該插入子樹的紅黑層數的情況是:黑-紅-紅。顯然最簡單的處理方式就是將其改為:紅-黑-紅。

處理:

  • 將父結點和叔父結點變為黑色
  • 將祖父結點變為紅色
  • 將祖父結點設定為當前插入結點

場景 4.2:叔父結點不存在或為黑色,插入結點的父結點是祖父結點的左子結點這種場景下,叔父結點所在的子樹的黑色結點就比父結點所在子樹的多,不滿足紅黑樹的性質 5。

場景 4.2.1:插入結點是左子樹

處理:

  • 將父結點變為黑色
  • 將祖父結點變為紅色
  • 將祖父結點右旋

場景 4.2.2:插入結點是左子樹

這種場景顯然可以轉換為 4.2.1。

處理:

  • 將父結點進行左旋
  • 將父結點設為插入結點,得到場景 4.2.1
  • 進行場景 4.2.1 的處理

場景 4.3:叔父結點不存在或為黑色,插入結點的父結點是祖父結點的右子結點相當於場景 4.2 的方向反轉,直接看圖。

場景 4.3.1:插入結點是左子樹

處理:

  • 將父結點變為黑色
  • 將祖父結點變為紅色
  • 對祖父結點進行左旋

場景 4.3.2:插入結點是右子樹

處理:

  • 將父結點進行右旋
  • 將父結點設定為插入結點,得到場景 4.3.1
  • 進行場景 4.3.1 的處理

下面舉個例子,往一棵紅黑樹中插入元素,整棵樹的變換如下圖所示:

8、紅黑樹刪除

紅黑樹刪除操作也分為兩步:

定位刪除的位置
定位刪除位置可以複用紅黑樹搜尋的操作。如果不存在目標結點,忽略本次操作;如果找到目標結點,刪除後進行自平衡處理。

刪除後實現自平衡二叉搜尋樹刪除的時候可能出現三種場景:

  • 若刪除結點無子結點,直接刪除即可。
  • 若刪除結點只有一個子結點,用子結點替換刪除結點。
  • 若刪除結點有兩個子結點,用**後繼結點(大於刪除結點的最小結點)**替換刪除結點。

另外兩種二叉樹的刪除場景都可以通過相互轉換變為場景一。
在場景二情況下:刪除結點用其唯一的子結點替換,子結點替換為刪除結點後,可以認為刪除的是子結點,若子結點又有兩個子結點,那麼相當於轉換為場景三,一直自頂向下轉換,總是能轉換為場景一。

在場景三情況下:刪除結點用後繼結點,如果後繼結點有右子結點,那麼相當於轉換為場景二,否則轉為場景一。

綜上所述,刪除的結點可以看作刪除替換結點,且替換結點最後總是在樹末。

紅黑樹刪除可能出現的所有場景:

為了方面理解,我們先約定一下結點的叫法:

  • R:替換結點
  • P:替換結點的父結點
  • S:替換結點的兄弟結點
  • SL:兄弟結點的左子結點
  • SR:兄弟結點的右子結點
  • 灰色:結點顏色可能是紅色,也可能是黑色

R是即將被替換到刪除結點的位置的替換結點,在刪除前,它還在原來所在位置參與樹的子平衡;平衡後再替換到刪除結點的位置,才算刪除完成

①場景 1:替換結點為紅色
我們把替換結點換到了刪除結點的位置時,由於替換結點為紅色,刪除也了不會影響紅黑樹的平衡,只要把替換結點的顏色變為刪除的結點的顏色即可重新平衡。

處理:替換結點顏色變為刪除結點的顏色。

②場景 2:替換結點為黑色當替換結點是黑色時,就必須進行自平衡處理了,我們可以通過區分替換結點是其父結點的左子結點還是右子結點,來做不同的旋轉,使樹重新平衡。

場景 2.1:替換結點是左子樹

場景 2.1.1:替換結點的兄弟結點為紅色

若兄弟結點是紅結點,那麼根據紅黑樹性質 4,兄弟結點的父結點和子結點肯定為黑色,按照下圖方式處理,得到刪除場景 2.1.2.3。

處理:

  • 將兄弟結點變為黑色
  • 將父結點變為紅色
  • 對父結點進行左旋,得到場景 2.1.2.3
  • 進行場景 2.1.2.3 的處理

場景 2.1.2:替換結點的兄弟結點為黑色
當兄弟結點為黑時,其父結點和子結點的具體顏色也無法確定,此時又得考慮多種子場景。

場景 2.1.2.1:替換結點的兄弟結點的右子結點為紅色,左子結點任意顏色即將刪除的左子樹的一個黑色結點,顯然左子樹的黑色結點少 1 了,然而右子結點又是紅色,那麼我們直接向右子樹“借”個紅結點來補充黑結點,並進行旋轉處理。

處理:

  • 將兄弟結點的顏色變為父結點的顏色
  • 將父結點變為黑色
  • 將兄弟結點的右子結點變為黑色
  • 對父結點進行左旋

場景 2.1.2.2:替換結點的兄弟結點的右子結點為黑色,左子結點為紅色
兄弟結點所在的子樹有紅結點,又可以向兄弟子樹“借”個紅結點過來,這就轉換回了場景 2.1.2.1。

處理:

  • 將兄弟結點變為紅色
  • 將兄弟結點的左子結點變為黑色
  • 對兄弟結點進行右旋,得到場景 2.1.2.1
  • 進行場景 2.1.2.1 的處理

場景 2.1.2.3:替換結點的兄弟結點的子結點都為黑色

兄弟子樹沒有紅結點可以“借”了,再向父結點“借”。如果父結點是黑色,為了讓父結點在所在的子樹中保證平衡(替換結點即將刪除,少了一個黑色結點,子樹也需要少一個)先把兄弟結點變為紅色,再讓父結點成為新的替換結點。

處理:

  • 如果父結點為黑色:將兄弟結點變為紅色;將父結點作為新的替換結點;重新進行刪除結點的場景處理。
  • 如果父結點為紅色:替換結點的父結點和替換結點的兄弟結點顏色交換;刪除結點和替換結點的值交換後,刪除替換結點。

場景 2.2:替換結點是右子樹實際上是場景 2.1 的映象操作。

場景 2.2.1:替換結點的兄弟結點為紅色

處理:

  • 將兄弟結點變為黑色
  • 將父結點變為紅色
  • 對父結點進行右旋,得到場景 2.2.2.3
  • 進行場景 2.2.2.3 的處理

場景 2.2.2:替換結點的兄弟結點為黑色

場景 2.2.2.1:替換結點的兄弟結點的左子結點為紅色,右子結點任意顏色

處理:

  • 將兄弟結點的顏色變為父結點的顏色
  • 將父結點變為黑色
  • 將兄弟結點的左子結點變為黑色
  • 對父結點進行右旋

場景 2.2.2.2:替換結點的兄弟結點的左子結點為黑色,右子結點為紅色

處理:

  • 將兄弟結點變為紅色
  • 將兄弟結點的右子結點設為黑色
  • 對兄弟結點進行左旋,得到場景 2.2.2.1
  • 進行場景 2.2.2.1 的處理

場景 2.2.2.3:替換結點的兄弟結點的子結點都為黑色

處理:

    • 如果父結點為黑色:將兄弟結點變為紅色;將父結點作為新的替換結點;重新進行刪除結點的場景處理。
    • 如果父結點為紅色:替換結點的父結點和替換結點的兄弟結點顏色交換;刪除結點和替換結點的值交換後,刪除替換結點。