數據結構與算法-學習筆記(18)

平衡二叉查找樹

平衡二叉查找樹的嚴格定義:二叉樹中任意一個節點的左右子樹的高度相差不能大於1。 bash

設計平衡二叉查找樹的初衷:爲了解決普通二叉查找樹在頻繁的插入、刪除等動態更新的狀況下(尤爲是插入的一組數據是有序的狀況),出現時間複雜度退化的問題。數據結構

可是在實際的開發中,並不能徹底嚴格的按照定義去作,只要保證平衡便可,也就是讓整棵樹左右看起來比較對稱、比較平衡、更新數據時不要形成一邊很高、一邊又很低的狀況。這樣就能讓整棵樹的高度相對來講低一些(如樹的高度是對數量級的,沒必要log2n大不少)。只要能保證這樣,就能夠說它是一個合格的平衡二叉查找樹。性能

平衡二叉查找樹-紅黑樹(R-B Tree)

紅黑樹中的節點,一類被標記爲黑色,一類被標記爲紅色。而且要求:spa

    1. 每一個節點不是黑色就是紅色,根節點必須是黑色的(能夠在節點數據結構中加一個數據字段表明顏色);
    1. 每一個葉子節點都是黑色的空節點(nil)(爲了保證除了根之外,每一個節點都有兄弟節點,也就是每一個父節點都有兩個叉),也就是說葉子節點不存儲數據;
    1. 任何相鄰(線連着)的節點都不能同時爲紅色,也就是說,紅色節點是被黑色節點隔開的;
    1. 每一個節點,從該節點到達其可達葉子節點的全部路徑,都包含相同數目的黑色節點(說明黑色節點是要成對出現的,不然失去平衡了)。

爲何說紅黑樹是「近似平衡的」

近似平衡就是說查找等動態更新的性能不會退化太嚴重。設計

二叉查找樹不少操做的性能都很樹的高度成正比,所以只要證實紅黑樹的高度能比較穩定的趨近log2n就好。3d

去掉紅色節點的黑色節點(無父節點則祖父節點代替,這樣防止本來黑色節點的高度發生改變)可能變成3叉樹/4叉樹。他們要比相同節點數相同的徹底二叉樹的高度要小,因此它的高度不超過log2n。

而紅色節點必須是被黑色節點隔開的,一紅一黑,所以紅色節點加回去,樹的高度應該不大於2log2n。高度僅是大了一倍,性能上降低很少,能夠近似O(logn)。code

同時紅黑樹是近似平衡,所以維護平衡的成本較低,性能比較穩定。對於工程應用來講,要面對各類異常狀況,穩定性很重要,所以紅黑樹的應用範圍較廣。cdn

動態數據結構

動態數據結構是指支持動態的更新操做,裏面存儲的數據是時刻在變化的,它支持查詢、刪除、插入等操做,而且這些操做是很是高效。這樣的數據結構才能算做動態數據結構。blog

如何維護紅黑樹的近似平衡

紅黑樹規定插入的節點必須是紅色的且在修改過程當中不能改變顏色,由於插入紅色節點比黑色節點違背規則的可能性更小。插入黑色節點必定會改變黑色高度(違背規則4),而插入紅色只有一半機會違背規則3。且3比4更易修正。開發

當插入一個新節點(二叉查找樹插入新節點會插到葉子節點上,那麼在紅黑樹上也就是插入到黑色空節點上層了)就可能會破壞紅黑樹本來結構,打破平衡,那麼如何修正呢?

主要有三種方式:改變節點顏色、左旋、右旋。

  1. 變色 假設本來只有節點E,而後插入了節點A和S,再插入F:

  2. 左旋 一般左旋操做用於將一個向右傾斜的紅色連接轉爲向左連接。

    動圖效果:

  3. 左旋

動圖效果:

紅黑樹的操做

  1. 節點數據
typedef struct TreeNode {
    int key;
    struct TreeNode *left;
    struct TreeNode *right;
    struct TreeNode *father; // 雙向鏈表
    BOOL color;
    
} Node;
複製代碼
  1. 左旋的實現
/*
 * 左旋示意圖:對節點x進行左旋
 *     p                       p
 *    /                       /
 *   x                       y
 *  / \                     / \
 * lx  y      ----->       x  ry
 *    / \                 / \
 *   ly ry               lx ly
 * 左旋作了三件事:
 * 1. 將y的左子節點賦給x的右子節點,並將x賦給y左子節點的父節點(y左子節點非空時)
 * 2. 將x的父節點p(非空時)賦給y的父節點,同時更新p的子節點爲y(左或右)
 * 3. 將y的左子節點設爲x,將x的父節點設爲y
 */
複製代碼
  1. 右旋的實現
/*
 * 左旋示意圖:對節點y進行右旋
 *        p                   p
 *       /                   /
 *      y                   x
 *     / \                 / \
 *    x  ry   ----->      lx  y
 *   / \                     / \
 * lx  rx                   rx ry
 * 右旋作了三件事:
 * 1. 將x的右子節點賦給y的左子節點,並將y賦給x右子節點的父節點(x右子節點非空時)
 * 2. 將y的父節點p(非空時)賦給x的父節點,同時更新p的子節點爲x(左或右)
 * 3. 將x的右子節點設爲y,將y的父節點設爲x
 */
複製代碼
  1. 插入操做
  • 若是第一次插入,本來樹是空的,則只須要將它塗黑便可。
  • 若是最終插入後,插入節點的父節點是黑色的,則不違反紅黑樹規則,不需改變。
  • 若是最終插入後,插入節點的父節點是紅色的,就違反規則了,須要修改保持樹的平衡了。此時又有三種狀況:
    • ①插入節點的父節點和其叔叔節點(祖父節點的另外一個子節點)均爲紅色的;
    • ②插入節點的父節點是紅色,叔叔節點是黑色,且插入節點是其父節點的右子節點;
    • ③插入節點的父節點是紅色,叔叔節點是黑色,且插入節點是其父節點的左子節點。

具體過程爲例: 插入新節點4,如圖1(也就是狀況①)。此時違反3,所以將它的父節點塗黑,可是若是隻塗5,就會形成黑色節點不平衡了,違反了4。所以新節點的父節點5和叔叔節點8都要塗黑。這時758都是黑色(黑色太多了?)將祖父7塗紅。這時又變成狀況②,把7當作新節點 ,以新節點 爲支點作左旋操做。如圖2->3。此時27節點仍有問題繼續修改,此時那2做爲新節點,將父節點7塗黑,祖父節點11塗紅。在以11爲支點右旋。這樣整棵樹就又平衡。

從上面的步驟能夠看出,若是插入數據最終是狀況1,則須要走完二、3步驟;若是插入數據出現的是狀況2,則只需走完3;插入後是3,則完成3便可了。

整體:變色->左旋->右旋

資料

相關文章
相關標籤/搜索