【Algorithms公開課學習筆記9】 符號表part2——平衡搜尋樹
Balanced Search Tree 平衡搜尋樹
0. 前言
上一篇文章,我們分析了二叉搜尋樹(BST傳送門)。在二叉搜尋樹中,查詢、插入、刪除、ceiling和floor等操作的平均時間效能取決於樹高。在極端情況下(如插入有序序列後),如果樹高h過大,其時間效能將會變得很差。根據樹的特點,通過平衡樹操作可以有效地降低樹的高度,從而達到理想的時間效能。這就是本篇文章的重點——平衡搜尋樹(BSTs)。
1. 2-3搜尋樹
基本概念
2-3樹的是由若干個2-節點(2-node)和3-節點(3-node)構成的樹。其基本特徵是有序對稱(symmetric order)和絕對平衡(perfect balance)。 2-節點
基本操作
查詢操作:對比節點key值,如果大於key,查右子樹;如果小於key,查左子樹;直到等於key或查到空連結。特別地,對於3-節點,如果小於左key,查左子樹;如果大於左key小於右key,查中子樹;如果大於右key,查右子樹;直到等於key或查到為空連結。 插入操作:查詢到該節點key值的合適位置,插入節點。如果該節點變成3-節點,無需操作;如果該節點變成4-節點,則要將中間key值的節點上浮到父節點,以此類推。在上浮過程中,如果root節點成為4-節點,則中間key值節點成為新root節點,左右key值節點分離成為新root節點的子節點。
下圖示意了三種上浮的情況:
效能分析
操作、插入、刪除操作均~clgN,其中係數c取決於實現方法(0< c <1) 分析所得,最壞的情況下是lgN,做好的情況下是log3N(約為0.631 lgN)。
存在問題
現實中,直接實現2-3樹是非常複雜的,主要有以下原因:
- 維持多種型別的節點是冗餘的(至少需要維持2-節點、3-節點和4-節點)
- 需要多種比較才能降低樹高
- 需要增加樹高以分離4-節點
- 分離節點時的情況太多
2. (左傾)紅黑樹
基本概念
上一節分析了2-3樹存在的問題就是實現起來非常複雜,因此引入了(左傾)紅黑樹來表示2-3樹。(這樣的話,實現紅黑樹就相當於實現了2-3樹)
在紅黑樹中,存在以下特徵:(結合下圖)
- 沒有節點連線兩條紅連結
- 每一條從根到空連結的路徑中黑連結的數量的相等的
- 紅連結都是左傾的
當理解紅黑樹的特徵之後,接著看看用紅黑樹來表示2-3樹的方法:將紅黑樹中左傾的紅連結水平放置,那麼紅連結所連線的兩節點就構成了2-3樹的3-節點,其他的構成2-節點。(結合下圖)
基本操作
節點表示Node
由於每一個節點僅有一個連結指向父節點,因此對此連結標色,以區別紅黑連結
private static final boolean RED = true;
private static final boolean BLACK = false;
private class Node{
Key key;
Value val;
Node left, right;
boolean color; // 父連結的標色
}
private boolean isRed(Node x){
if (x == null) return false;
return x.color == RED;
}
左旋left rotation
private Node rotateLeft(Node h){
assert isRed(h.right);//判斷右連結是否紅,即是否右傾的情況
Node x = h.right;
h.right = x.left;
x.left = h;
x.color = h.color;
h.color = RED;
return x;
}
右旋right rotation
private Node rotateRight(Node h){
assert isRed(h.left);//判斷左連結是否紅,即是否左傾的情況
Node x = h.left;
h.left = x.right;
x.right = h;
x.color = h.color;
h.color = RED;
return x;
}
跳色color flip
private void flipColors(Node h){
assert !isRed(h);//判斷
assert isRed(h.left);
assert isRed(h.right);
h.color = RED;
h.left.color = BLACK;
h.right.color = BLACK;
}
通過左旋、右旋、跳色等操作,可以保持紅黑樹的對稱有序和絕對平衡的特點。
查詢
基於紅黑樹實現的BSTs的查詢操作與基本的BST的查詢操作是一致的。
public Val get(Key key){
Node x = root;
while (x != null){
int cmp = key.compareTo(x.key);
if (cmp < 0) x = x.left;
else if (cmp > 0) x = x.right;
else return x.val;
}
return null;
}
插入
當插入一個節點(節點C)到一棵紅黑樹中,為了保持絕對平衡和對稱有序的特點,存在以下情況:
插入的節本操作可以描述成:(結合下圖)
- 基本的BST插入操作,同時新插入的節點的附連結標成紅色
- 如果需要的話,左右旋轉去平衡4-節點(一個節點同時連線兩個紅連結)
- 通過跳色操作將紅連結往上一層傳遞
- 如果需要的話,旋轉以保持所有的紅連結左傾
- 迴圈操作直到滿足紅黑樹的所有特徵
總結起來,在遞迴的場景下,就是以下三種情況:
- 右連結紅,左連結黑:左旋
- 左連結紅,左子節點的左連結紅:右旋
- 左右連結紅:跳色
private Node put(Node h, Key key, Value val){
if (h == null) return new Node(key, val, RED);//插入節點(遞迴出口)
int cmp = key.compareTo(h.key);
if (cmp < 0) h.left = put(h.left, key, val);
else if (cmp > 0) h.right = put(h.right, key, val);
else h.val = val;
if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h);
if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h);
if (isRed(h.left) && isRed(h.right)) flipColors(h);
return h;
}
效能分析
通過紅黑樹實現的BST,其樹高在通常情況下 ~lgN,在最壞的情況下不超過 ~2lgN。
應用
JAVA:java.util.TreeMap ,java.util.TreeSet等資料結構 C++ STL:map, mutilmap, mutilset等資料結構 linux核心
3. B樹
基本概念
B樹是2-3樹的泛化:在B樹中,每個節點允許有M-1個子節點(子連結)。(M的取值視實際情況而定) B樹的特徵如下:(結合下圖)
- 跟節點至少有兩個子連結
- 其他節點至少有M/2個子連結
- 外部節點包含內部節點的key(葉節點就是外部節點)
- 內部節點包含每個外部節點的首個key(用於索引)
基本操作
查詢
- 從根節點開始
- 通過內部節點對比key,確定對應的連結
- 通過連結確定儲存key的外部節點
插入
- 先查詢新key的位置
- 插入
- 如果溢位,則要分離節點,並將首key儲存在上一級的內部節點
應用
B樹被廣泛地應用在各作業系統的檔案系統和資料庫中。如windows的NTFS、Mac的HFS和SQL、ORACLE等各種主流資料庫。