红黑树-查找-插入
1. 简述
今天主要复习一下红黑树的性质和插入操作。
红黑树的结点与BST和AVL不同的是,还包含了父节点指针。
2. 性质
第一,根结点和叶子结点(叶子是NIL结点)是黑色的,其他的中间结点是红色或者黑色的。
第二,从根到每个叶子的路径上的黑色结点个数相同。
第三,红色结点的孩子都是黑色的(不能连续出现两个红色的结点)。
注意:红黑树的性质中所说的叶子结点是指外部结点,在没有外部结点的二叉搜索树中,对应的是叶子结点下面的空结点,即NULL。以下图为例:
图中的叶子结点为NIL,实际上,如果不算空结点,那么6号结点是叶子结点。
3. 查找
在只读操作上,红黑树、AVL树和普通的二叉搜索树都是一样的。查找操作要么向左,要么向右,要么找到,如果从根一直到叶子都没找到,那么就是查找失败。
while(node != NULL) {
if(value < node->value) // 向左
node = node->left;
else if(value > node->value) // 向右
node = node->right;
else // 找到
return node;
}
return NULL; // 失败
}
4. 插入
红黑树的插入大体思路是,首先找到要插入的位置,插入结点,标记为红色,然后根据情况判断是否打破了性质,然后,进行修正。如果初始标记为黑色,那么就打破了每条路径上黑结点相同的这一性质,修正起来比较麻烦。
首先,建立新结点,标记为红色,然后根据普通BST的插入方法,找到合适位置插入。令指针P指向新结点。考察如下五种情况:
情况一:P->parent==NULL。
这说明当前红黑树中只有这一个结点,修改其标记为黑色即可。
情况二,P->parent!=NULL,且P->parent是黑色的。
这说明不会出现连续的两个红色结点,其他性质不会因为插入一个新结点而破坏。因此,不需要做任何改变。
情况三,P->parent!=NULL,且P->parent是红色的,且P的叔父结点也是红色的。
修改P->parent->parent的颜色为红色,修改P->parent->left和P->parent->right的颜色为黑色。
修改P=p->parent->parent,进入情况一,考察这个新的P结点,即旧的P的祖父。
情况四,P->parent!=NULL,且P->parent是红色的,且P的叔父结点是黑色的,且P的祖父与P的关系为LR或者RL型。
LR型指,P的父节点在P的祖父结点的左边,P结点在P的父节点的右边。RL型刚好相反。
LR型可以用如下代码检查 P->value<P->parent->parent->value且p->value>P->parent->value
如果是LR型,对P->parent进行左旋,变成LL型,如果是RL型,对P->parent进行右旋,变成RR型。(旋转后要额外要修改新的子树的根与子树父节点的链接关系)。如果得到LL型,将新子树的左孩子,传入情况五处理。如果得到RR型,将,新子树的右孩子,传入情况五处理。
情况五,P->parent!=NULL,且P->parent是红色的,且P的叔父是黑色的,且P的祖父与P的关系为LL或者RR型。
修改P->parent为黑色,修改P->parent->parent为红色,然后对于LL型,右旋P的祖父,对于RR型左旋P的祖父(旋转后要额外要修改新的子树的根与子树父节点的链接关系)。旋转后,子树的新根即为旋转前的P->parent,现在已经标记为黑色了,所以可以结束循环。
#include<stack>
using namespace std;
#define RED 0
#define BLACK 1
struct RBNode {
RBNode* left;
RBNode* right;
RBNode* parent;
int color;
int value;
};
// 左左情况右旋,返回子树旋转后的根
RBNode* LL(RBNode* A) {
assert(NULL != A);
RBNode* B = A->left;
A->left = B->right;
B->right = A;
A->parent = B;
return B;
}
// 右右情况左旋,返回子树旋转后的根
RBNode* RR(RBNode* A) {
assert(NULL != A);
RBNode* B = A->right;
A->right = B->left;
B->left = A;
A->parent = B;
return B;
}
bool rb_insert(RBNode*& root, int value) { // root要求是指针的引用或者指针的指针
RBNode *p, *pre, *tmp;
tmp = root;
// 寻找插入位置,并且保证路径
pre = NULL;
while(tmp != NULL) {
pre = tmp;
if(value < tmp->value)
tmp = tmp->left;
else if(value > tmp->value)
tmp = tmp->right;
else // 插入识别
return false;
}
// 建立新结点
p = new RBNode;
p->left = p->right = NULL;
p->color = RED;
p->value = value;
// 插入新结点
if(pre == NULL) { // 根结点为空
p->parent = NULL;
p->color = BLACK;
root = p;
return true;
}
// 根结点不为空
p->value<pre->value ? (pre->left=p):(pre->right=p);
p->parent = pre;
// 五种情况
while(NULL != p) {
if(p->parent == NULL) { // 情况一:P为根结点
p->color = BLACK;
return true;
}
else if(p->parent->color == BLACK) { // 情况二:P的父结点是黑色的
return true;
}
else { // P的父结点是红色
RBNode* father = p->parent;
RBNode* grandfather = p->parent->parent;
RBNode* uncle = father->value<grandfather->value?grandfather->right:grandfather->left;
if(uncle!=NULL && uncle->color==RED) { // 情况三:P的父结点和叔父结点都是红色的
grandfather->color = RED;
father->color = BLACK;
uncle->color = BLACK;
p = grandfather; // 进入循环
}
else if( (father->value<grandfather->value && p->value>father->value)
|| (father->value>grandfather->value && p->value<father->value) ) {
// 情况四:p与其祖父结点关系为LR型或者RL型
if(p->value>father->value) { // LR,左旋
tmp = RR(father);
grandfather->left = tmp;
tmp->parent = grandfather;
}
else { // RL,右旋
tmp = LL(father);
grandfather->right = tmp;
tmp->parent = grandfather;
}
// 进入循环,此时p与其祖父结点关系为LL或者RR型,肯定会进入情况五
}
else { // 情况五:p与其祖父结点关系为LL型或者RR型
grandfather->color = RED;
father->color = BLACK;
RBNode* tmp_father = grandfather->parent;
if(p->value<father->value) { // LL
tmp = LL(grandfather);
tmp->parent = tmp_father;
}
else { // RR
tmp = RR(grandfather);
tmp->parent = tmp_father;
}
if(tmp_father != NULL)
tmp->value<tmp_father->value?tmp_father->left=tmp:tmp_father->right=tmp;
else
root = tmp;
return true;
}
}
}
}
void print_father_child(RBNode* root) {
stack<RBNode*> store;
store.push(root);
while(!store.empty()) {
root = store.top();
store.pop();
cout << root->value << " : ";
if(root->color == RED)
cout << " RED, ";
else
cout << " BLACK, ";
if(root->left==NULL)
cout << "NULL";
else
cout << root->left->value;
cout << " , ";
if(root->right==NULL)
cout << "NULL";
else {
cout << root->right->value;
}
cout << endl;
if(root->right)
store.push(root->right);
if(root->left)
store.push(root->left);
}
}
int main() {
RBNode* root = NULL;
for(int i=0; i<10; i++)
rb_insert(root, i);
print_father_child(root);
system("PAUSE");
return 0;
}
实际上,循环的情况只有进入情况3后,继续进入情况3,如此往复直到根结点,一旦进入情况1,2,5后直接结束,在进入情况4后,下次循环必然进入情况5。
输出结果如下:
5. 参考
维基百科_红黑树 http://zh.wikipedia.org/wiki/%E7%BA%A2%E9%BB%91%E6%A0%91