平衡二叉查找樹的嚴格定義:二叉樹中任意一個節點的左右子樹的高度相差不能大於1。 bash
設計平衡二叉查找樹的初衷:爲了解決普通二叉查找樹在頻繁的插入、刪除等動態更新的狀況下(尤爲是插入的一組數據是有序的狀況),出現時間複雜度退化的問題。數據結構
可是在實際的開發中,並不能徹底嚴格的按照定義去作,只要保證平衡便可,也就是讓整棵樹左右看起來比較對稱、比較平衡、更新數據時不要形成一邊很高、一邊又很低的狀況。這樣就能讓整棵樹的高度相對來講低一些(如樹的高度是對數量級的,沒必要log2n大不少)。只要能保證這樣,就能夠說它是一個合格的平衡二叉查找樹。性能
紅黑樹中的節點,一類被標記爲黑色,一類被標記爲紅色。而且要求:spa
近似平衡就是說查找等動態更新的性能不會退化太嚴重。設計
二叉查找樹不少操做的性能都很樹的高度成正比,所以只要證實紅黑樹的高度能比較穩定的趨近log2n就好。3d
而紅色節點必須是被黑色節點隔開的,一紅一黑,所以紅色節點加回去,樹的高度應該不大於2log2n。高度僅是大了一倍,性能上降低很少,能夠近似O(logn)。code
同時紅黑樹是近似平衡,所以維護平衡的成本較低,性能比較穩定。對於工程應用來講,要面對各類異常狀況,穩定性很重要,所以紅黑樹的應用範圍較廣。cdn
動態數據結構是指支持動態的更新操做,裏面存儲的數據是時刻在變化的,它支持查詢、刪除、插入等操做,而且這些操做是很是高效。這樣的數據結構才能算做動態數據結構。blog
紅黑樹規定插入的節點必須是紅色的且在修改過程當中不能改變顏色,由於插入紅色節點比黑色節點違背規則的可能性更小。插入黑色節點必定會改變黑色高度(違背規則4),而插入紅色只有一半機會違背規則3。且3比4更易修正。開發
當插入一個新節點(二叉查找樹插入新節點會插到葉子節點上,那麼在紅黑樹上也就是插入到黑色空節點上層了)就可能會破壞紅黑樹本來結構,打破平衡,那麼如何修正呢?
主要有三種方式:改變節點顏色、左旋、右旋。
變色 假設本來只有節點E,而後插入了節點A和S,再插入F:
左旋 一般左旋操做用於將一個向右傾斜的紅色連接轉爲向左連接。
左旋
typedef struct TreeNode {
int key;
struct TreeNode *left;
struct TreeNode *right;
struct TreeNode *father; // 雙向鏈表
BOOL color;
} Node;
複製代碼
/*
* 左旋示意圖:對節點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
*/
複製代碼
/*
* 左旋示意圖:對節點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
*/
複製代碼
具體過程爲例: 插入新節點4,如圖1(也就是狀況①)。此時違反3,所以將它的父節點塗黑,可是若是隻塗5,就會形成黑色節點不平衡了,違反了4。所以新節點的父節點5和叔叔節點8都要塗黑。這時758都是黑色(黑色太多了?)將祖父7塗紅。這時又變成狀況②,把7當作新節點 ,以新節點 爲支點作左旋操做。如圖2->3。此時27節點仍有問題繼續修改,此時那2做爲新節點,將父節點7塗黑,祖父節點11塗紅。在以11爲支點右旋。這樣整棵樹就又平衡。
從上面的步驟能夠看出,若是插入數據最終是狀況1,則須要走完二、3步驟;若是插入數據出現的是狀況2,則只需走完3;插入後是3,則完成3便可了。
整體:變色->左旋->右旋