1. 2-3-4樹介紹
2-3-4是四階的B樹,他屬于一種多路查找樹,他的結(jié)構(gòu)有以下限制:
所有葉子結(jié)點都擁有相同的深度。
結(jié)點只能是2-結(jié)點,3-結(jié)點,4-結(jié)點之一。
- 2-結(jié)點:包含1個元素的結(jié)點,有2個子結(jié)點。
- 3-結(jié)點:包含2個元素的結(jié)點,有3個子結(jié)點。
-
4-結(jié)點:包含3個元素的結(jié)點,有4個子結(jié)點。
所有節(jié)點必須至少包含1個元素。
元素始終保持排序順序,整體上保持二叉查找樹的性質(zhì),即父結(jié)點大于左子樹結(jié)點,小于右子樹結(jié)點。而且結(jié)點有多個元素時,每個元素大于他左邊和他左子樹的元素。
下圖是一個典型的2-3-4樹:
2-3-4樹
2-3-4樹的生成
234樹添加數(shù)據(jù),有兩種情況。
第一種:添加數(shù)據(jù)的節(jié)點的數(shù)據(jù)個數(shù)小于3個直接添加。
第二種:添加數(shù)據(jù)的節(jié)點的數(shù)據(jù)個數(shù)等于3個,需要將節(jié)點的3個數(shù)據(jù)分成3個234樹節(jié)點,中間的數(shù)據(jù)成為左右兩邊數(shù)據(jù)的雙親節(jié)點,然后根據(jù)添加數(shù)據(jù)的大小加到左右兩邊的子節(jié)點中。
這種向上分裂添加數(shù)據(jù)的有點在于能夠保證左右子樹高度一致,無需平衡234樹的左右高度。
2-3-4樹和紅黑樹的等價關(guān)系
2-3-4樹轉(zhuǎn)化為紅黑樹
2. 紅黑樹
紅黑樹,Red-Black Tree「RBT」是一個自平衡(不是絕對的平衡)的二叉查找樹(BST),樹上的每個節(jié)點都遵循下面的規(guī)則:
1.每個節(jié)點要么是黑色,要么是紅色。
2.根節(jié)點是黑色。
3.每個葉子節(jié)點(NIL)是黑色。
4.每個紅色結(jié)點的兩個子結(jié)點一定都是黑色。
5.任意一結(jié)點到每個葉子結(jié)點的路徑都包含數(shù)量相同的黑結(jié)點。
紅黑樹能自平衡,它靠的是什么?三種操作:左旋、右旋和變色
enum RBColor{
BLACK,
RED
};
typedef int KEY_TYPE;
typedef struct RBNode{
struct RBNode *parent;
struct RBNode *left;
struct RBNode *right;
enum RBColor color;
KEY_TYPE key;
void *value;
}RBNode, *RBTree;
//左旋轉(zhuǎn)
void leftRotate(RBTree *root,RBNode *node)
{
RBNode *parent = node->parent;
RBNode *right = node->right;
RBNode *rleft = right->left;
right->left = node;
node->parent = right;
node->right = rleft;
if(rleft)
rleft->parent = node;
right->parent = parent;
if(parent == NULL){
*root = right;
}else if(parent->left == node)
parent->left = right;
else if (parent->right == node)
parent->right = right;
}
//右旋轉(zhuǎn)
void rightRotate(RBTree *root,RBNode *node)
{
RBNode *parent = node->parent;
RBNode *left = node->left;
RBNode *lright = left->right;
left->right = node;
node->parent = left;
node->left = lright;
if(lright)
lright->parent = node;
left->parent = parent;
if(parent == NULL)
*root = left;
else if (parent->left == node)
parent->left = left;
else if (parent->right == node)
parent->right = left;
}
enum RBColor colorOf(RBNode *node)
{
return node!=NULL ? node->color : BLACK;
}
void setColor(RBNode *node, enum RBColor color)
{
if(node == NULL)
return;
node->color = color;
}
插入操作
1. 3結(jié)點插入結(jié)點情況
以下情況需要調(diào)整:
- 第二種先左旋然后跟第一種情況相同
- 后兩種調(diào)整情況跟這兩種相同,只是方向相反而已。
2. 4結(jié)點插入子結(jié)點情況
- 這些情況都需要調(diào)整
- 其他三種情況都是將父結(jié)點和叔叔結(jié)點變成黑色,爺爺結(jié)點變紅色
void fixAfterInsert(RBTree *root,RBNode* node);
//二叉樹插入操作,非遞歸
void insert(RBTree root,KEY_TYPE key, void *value)
{
RBNode *t = root;
if(t == NULL)
{
root = (RBNode *)malloc(sizeof(RBNode));
root->key = key;
root->value = value;
root->parent = NULL;
return;;
}
int cmp;
//1.找到插入位置(找到新增結(jié)點的父結(jié)點)
RBNode *parent;
do {
parent = t;
cmp = key - t->key;
if(cmp < 0)
t = t->left;
else if (cmp > 0)
t = t->right;
else
{
t->value = value;
return;
}
} while (t != NULL);
RBNode *node = (RBNode *)malloc(sizeof(RBNode));
node->key = key;
node->value = value;
node->parent = parent;
if(cmp < 0)
parent->left = node;
else
parent->right = node;
//旋轉(zhuǎn)和變色 調(diào)整紅黑樹的平衡
fixAfterInsert(&root, node);
}
void fixAfterInsert(RBTree *root,RBNode* node)
{
node->color = RED;
// 2結(jié)點不用調(diào)整,3 4結(jié)點才需要調(diào)整
while(node != NULL && node != *root && node->parent->color == RED){
if(node->parent == node->parent->parent->left){
//需要調(diào)整的只剩下4種情況,有叔叔節(jié)點和沒有叔叔節(jié)點
RBNode *uncle = node->parent->parent->right;
if(colorOf(uncle) == RED){
//叔叔節(jié)點如果是紅色,表示有叔叔節(jié)點
//變色+循環(huán)
//父親和叔叔變?yōu)楹谏?爺爺變?yōu)榧t色
setColor(node->parent, BLACK);
setColor(uncle, BLACK);
setColor(node->parent->parent, RED);
//循環(huán)往上處理
node = node->parent->parent;
}else{ //沒有叔叔節(jié)點
if(node == node->parent->right){
//如果是右節(jié)點需要將父結(jié)點向左旋轉(zhuǎn)
node = node->parent;
leftRotate(root, node);
}
//父親變?yōu)楹谏?爺爺變?yōu)榧t色
setColor(node->parent, BLACK);
setColor(node->parent->parent, RED);
//爺爺結(jié)點向右旋轉(zhuǎn)
rightRotate(root, node->parent->parent);
}
}else{
//需要調(diào)整的也是4種情況,剛好和上面4種情況左右相反
RBNode *uncle = node->parent->parent->left;
if(colorOf(uncle) == RED){
setColor(node->parent, BLACK);
setColor(uncle, BLACK);
setColor(node->parent->parent, RED);
//循環(huán)往上處理
node = node->parent->parent;
}else{
if(node == node->parent->left){
node = node->parent;
rightRotate(root, node);
}
//父親變?yōu)楹谏?爺爺變?yōu)榧t色
setColor(node->parent, BLACK);
setColor(node->parent->parent, RED);
//爺爺結(jié)點向左旋轉(zhuǎn)
leftRotate(root, node->parent->parent);
}
}
}
setColor(*root,BLACK);
}
紅黑樹的刪除操作
- 刪除規(guī)則按二叉搜索樹的刪除規(guī)則:
- 要刪除的結(jié)點是葉子結(jié)點:直接刪除,并將父結(jié)點指針置為NULL
- 要刪除的結(jié)點有一個孩子結(jié)點:刪除該結(jié)點,并將父結(jié)點的指針指向這個孩子結(jié)點
- 要刪除的結(jié)點有左右子樹:用另一個結(jié)點替代被刪除的結(jié)點:左子樹最大元素或右子樹的最小元素
-
紅黑樹刪除和調(diào)整:
二叉樹的刪除轉(zhuǎn)換為2-3-4樹的刪除刪除的其實就是2-3-4樹的最底層
其實就是刪除2節(jié)點3節(jié)點4節(jié)點
刪除3和4節(jié)點的時候可以通過本身的替換處理掉但是刪除2節(jié)點的時候,自己是不能夠處理的
- 刪除節(jié)點自己能夠搞定
- 刪除節(jié)點自己搞不定,向兄弟節(jié)點借 兄弟節(jié)點借
- 刪除節(jié)點自己搞不定,向兄弟節(jié)點借 兄弟節(jié)點不借
情況2的調(diào)整
情況3的調(diào)整
RBNode *Find(RBTree root, KEY_TYPE key)
{
RBNode *node = root;
while (node) {
if(key < node->key)
node = node->left;
else if (key > node->key)
node = node->right;
else
break;
}
return node;
}
RBNode *FindMax(RBNode *RBT)
{
if(RBT)
while (RBT->right) {
RBT = RBT->right;
}
return RBT;
}
RBNode *FindMin(RBNode *RBT)
{
if(RBT)
while (RBT->left) {
RBT = RBT->left;
}
return RBT;
}
void fixAfterDelete(RBTree root,RBNode *node);
void* delete(RBTree *root, KEY_TYPE key)
{
RBNode *node = Find(*root, key);
RBNode *tmp ,*parent,*child=NULL;
if(node == NULL)
return NULL;
void *oldValue = node->value;
if(node->left != NULL && node->right != NULL)
{
RBNode *rightMin = FindMin(node->right);
node->key = rightMin->key;
node->value = rightMin->value;
node = rightMin;
}
tmp = node;
parent = node->parent;
child = node->left ? node->left : node->right;
if(child){
child->parent = parent;
if(parent == NULL)
*root = child;
else if(parent->left == node)
parent->left = child;
else if(parent->right == node)
parent->right = child;
if(colorOf(node) == BLACK)
{
fixAfterDelete(*root,child);
}
}else{
if(colorOf(node) == BLACK)
{
fixAfterDelete(*root,node);
}
if(parent == NULL)
*root = child;
else if(parent->left == node)
parent->left = child;
else if(parent->right == node)
parent->right = child;
}
free(node);
return oldValue;
}
/**
刪除結(jié)點后的調(diào)整工作
2-3-4樹刪除操作
1. 刪除3/4結(jié)點 自己能搞定的
2. 刪除2結(jié)點 自己搞不定,需要兄弟節(jié)點借,兄弟節(jié)點借
父親結(jié)點下去,兄弟節(jié)點找個結(jié)點替換父親結(jié)點位置
3. 刪除2結(jié)點 自己搞不定,需要兄弟借 兄弟不接
*/
void fixAfterDelete(RBTree root,RBNode *node)
{
//情況2和3
while (node != root && colorOf(node) == BLACK){
//判斷node是父結(jié)點的左孩子還是右孩子
if(node == node->parent->left){
//1.找到兄弟節(jié)點
RBNode *rNode = node->parent->right;
//判斷是不是真正的兄弟節(jié)點
if(colorOf(rNode) == RED){
//不是真正的兄弟結(jié)點 變色 旋轉(zhuǎn)
setColor(rNode, BLACK);
setColor(node->parent, RED);
leftRotate(&root, node->parent);
rNode = node->parent->right;
}
//當(dāng)兄弟結(jié)點一個子結(jié)點都沒有 不借
if(colorOf(rNode->left) == BLACK && colorOf(rNode->right) == BLACK){
//3.兄弟結(jié)點 不借
setColor(rNode, RED);
node = node->parent;
}else{
//2.兄弟結(jié)點 借
//如果兄弟節(jié)點的子結(jié)點是其左子結(jié)點 需要先變色 右轉(zhuǎn)一次
if(colorOf(rNode->right) == BLACK){
//右子節(jié)點為空,左節(jié)點不為空
setColor(rNode->left, BLACK);
setColor(rNode, RED);
rightRotate(&root, rNode);
rNode = node->parent->right;
}
//需要根據(jù)父結(jié)點做一次左旋操作 變色
setColor(rNode, colorOf(node->parent));
setColor(node->parent, BLACK);
setColor(rNode->right, BLACK);
leftRotate(&root, node->parent);
node = root;
}
}else{
//1.找到兄弟節(jié)點
RBNode *lNode = node->parent->left;
//判斷是不是真正的兄弟節(jié)點
if(colorOf(lNode) == RED){
//不是真正的兄弟結(jié)點 變色 旋轉(zhuǎn)
setColor(lNode, BLACK);
setColor(node->parent, RED);
leftRotate(&root, node->parent);
lNode = node->parent->left;
}
//當(dāng)兄弟結(jié)點一個子結(jié)點都沒有 不借
if(colorOf(lNode->left) == BLACK && colorOf(lNode->right) == BLACK){
//3.兄弟結(jié)點 不借
setColor(lNode, RED);
node = node->parent;
}else{
//2.兄弟結(jié)點 借
//如果兄弟節(jié)點的子結(jié)點是其右子結(jié)點 需要先變色 左轉(zhuǎn)一次
if(colorOf(lNode->left) == BLACK){
//左子節(jié)點為空,右節(jié)點不為空
setColor(lNode->right, BLACK);
setColor(lNode, RED);
leftRotate(&root, lNode);
lNode = node->parent->left;
}
//需要根據(jù)父結(jié)點做一次左旋操作 變色
setColor(lNode, colorOf(node->parent));
setColor(node->parent, BLACK);
setColor(lNode->right, BLACK);
rightRotate(&root, node->parent);
node = root;
}
}
}
//情況1:替換的結(jié)點為紅色 我們只需要變色為黑色即可
setColor(node, BLACK);
}