紅黑樹原理與實現
阿新 • • 發佈:2020-07-16
紅黑樹
紅黑樹的性質
-
根節點必須是黑色
-
每個結點必須是黑色或者紅色
-
葉子節點 (nil) 是黑色
-
如果一個結點是紅色,則它的兩個子節點都是黑色的
-
從根結點出發到所有葉節點的路徑上,黑色節點數量相同
#define K(n) ((n)->key) #define C(n) ((n)->color) #define L(n) ((n)->lchild) #define R(n) ((n)->rchild) //定義紅黑樹結點 typedef struct node { int key; int color; //紅色0 黑色1 雙重黑2 struct node *lchild, rchild; } node; //定義nil結點 node __nil; #define nil (&__nil) __attribute__((constructor)) void init_nil() { nil->key = 0; nil->color = 1; nil->lchild = nil->rchild = nil; } //建立新節點 node *getNewNode(int key) { node *p = (node *)malloc(sizeof(node)); p->key = key; p->lchild = p->rchild = nil; p->color = 0; //預設插入紅色結點 return p; } //紅黑樹的刪除 void clear(node *root) { if (root == nil) return; clear(root->lchild); clear(root->rchild); free(root); return; } //輔助函式 //是否有紅色孩子結點 int hasRed(node *root) { return C(L(root)) == 0 || C(R(root)) == 0; } //找到前驅節點 node *predecessor(node *root) { node *p = root->lchild; while (p->rchild != nil) p = p->rchild; return p; }
紅黑樹的調整策略
- 插入調整站在祖父結點看
- 刪除調整站在父結點看
- 插入和刪除的情況處理一共五種
紅黑樹結點的插入
情況一
兩個孩子結點均為紅色,孩子的孩子結點有紅色。把孩子改為黑色,自己改為紅色(所謂的紅色上頂)
情況二
LL型調整先進行大右旋,然後有兩種變色方案(只需要保證這兩層的黑色結點為一就可以),上黑下紅(原來的結點變為黑色,父結點變為紅色),或者上紅下黑(左孩子結點變為黑色)。
LR型調整先進行區域性小左旋,然後就變為LL型的情況。
RR型和RL型類似於LL型與LR型,不多贅述。
//左旋 node *left_rot(node *root) { node *p = root->rchild; root->rchild = p->lchild; p->lchild = root; return p; } //右旋 node *right_rot(node *root) { node *p = root->lchild; root->lchild = p->rchild; p->rchild = root; return p; } node *insert_maintain(node *root) { if (!hasRed(root)) return root; //不可能出現雙紅 int flag = 0; if (C(L(root)) == 0 && hasRed(L(root))) flag = 1; else if (C(R(root)) == 0 && hasRed(R(root))) flag = 2; if (!flag) return root; if (flag == 1 && C(R(root)) == 1) { //第二種情況 if (C(R(L(root)))) == 0) { root->lchild = left_rot(root->lchild); } root = right_rot(root); } else { if (C(L(R(root)))) == 0) { root->rchild = right_rot(root->rchild); } root = left_rot(root); } C(root) = 0; //這裡採用紅色上頂方案,兩種情況最終變色方案一樣 C(L(root)) = C(R(root)) = 1; return root; } node *__insert(node *root, int key) { if (root == nil) return getNewNode(key); //建立新節點 if (root->key == key) return root; if (root->key > key) L(root) = __insert(L(root), key); //在左子樹中插入key else R(root) = __insert(R(root), key); return insert_maintain(root); //進行插入調整 } node *insert(node *root, int key) { __insert(root, key); C(root) = 1; //插入之後根節點變為黑色,保證根節點為黑色,否則可能為紅色 return root; }
一個栗子
紅黑樹結點的刪除
刪除的結點度為1
由於紅黑樹的性質,從任意節點到葉子結點經過的黑色節點數目相同,可以得知,度為1的結點一定是黑色結點。如果一個紅色結點的度為1,那麼它的孩子一定是黑色結點,這樣就不符合紅黑樹的性質。
刪除的結點度為0
第一種情況:直接刪除紅色結點即可。
第二種情況:刪除x結點會造成紅黑樹的不平衡,這時候引入雙重黑的概念,在nil結點上增加一層黑色,相當於兩個黑結點,然後再調整雙重黑結點即可。
雙重黑結點的刪除
情況一
雙重黑結點的兄弟結點為黑色,兄弟節點的孩子全為黑色。這時候把兄弟節點和自己黑色減一,父結點黑色加一即可。
情況二
RR型,先對38結點進行左旋,把28顏色改為正常,這時候由於48結點的顏色不確定,需要把38設定為黑色,72設定為黑色,51設定為38的顏色。
情況三
RL型,對72結點進行右旋,72變為紅色,51變為黑色,然後按照情況二處理
情況四
雙重黑結點兄弟結點為紅色時,左孩子為紅色則右旋,右孩子為紅色則左旋,原來的根節點變為紅色,旋轉後的根節點變為黑色。然後進入相應的子樹中處理二重黑結點。(假裝這裡有圖)
node *erase_maintain(root) {
if (C(L(root)) != 2 && C(R(root)) != 2) return root;
if (hasRed(root)) { //情況四
int flag = 0;
root->color = 0;
if (C(L(root)) == 0) root = right_rot(root), flag = 1;
else if (C(R(root)) == 0) root = left_rot(root), flag = 2;
root->color = 1;
if (flag == 1) root->rchild = erase_maintain(root->rchild);
else root->lchild = erase_maintain(root->lchild);
return root;
}
if (C(L(root)) == 1) {
C(R(root)) = 1;
if (!hasRed(L(root))) {
C(root) += 1;
C(L(root)) -= 1;
return root;
}
if (C(L(L(root))) != 0) {
C(L(root)) = 0;
root->lchild = left_rot(root->lchild);
C(L(root)) = 1;
}
C(L(root)) = C(root);
root = right_rot(root);
C(L(root)) = 1;
C(R(root)) = 1;
} else {
C(L(root)) = 1;
if (!hasRed(R(root))) {
root->color += 1;
C(R(root)) -= 1;
return root;
}
if (C(R(R(root))) != 0) {
C(R(root)) = 0;
root->rchild = right_rot(root->rchild);
C(R(root)) = 1;
}
C(R(root)) = C(root);
root = left_rot(root);
C(L(root)) = 1;
C(R(root)) = 1;
}
return root;
}
node *__erase(node *root, int key) {
if (root == nil) return root;
if (root->key > key) root->lchild = __erase(root, key);
else if (root->key < key) root->rchild = __erase(root, key);
else {
if (root->lchild == nil || root->rchild == nil) {
node *p = root->lchild == nil ? root->rchild : root->rchild;
p->color += root->color; //here
free(root);
return p;
} else {
node *p = decessor(root);
root->key = p->key;
root->lchild = __erase(root->lchild, p->key);
}
}
return erase_maintain(root);
}
node *erase(node *root, int key) {
root = __erase(root, key);
root->color = 1;
return root;
}