红黑树-查找-插入

1. 简述

    今天主要复习一下红黑树的性质和插入操作。
    红黑树的结点与BST和AVL不同的是,还包含了父节点指针。

2. 性质

    第一,根结点和叶子结点(叶子是NIL结点)是黑色的,其他的中间结点是红色或者黑色的。
    第二,从根到每个叶子的路径上的黑色结点个数相同。
    第三,红色结点的孩子都是黑色的(不能连续出现两个红色的结点)。
    注意:红黑树的性质中所说的叶子结点是指外部结点,在没有外部结点的二叉搜索树中,对应的是叶子结点下面的空结点,即NULL。以下图为例:
   
    图中的叶子结点为NIL,实际上,如果不算空结点,那么6号结点是叶子结点。

3. 查找

    在只读操作上,红黑树、AVL树和普通的二叉搜索树都是一样的。查找操作要么向左,要么向右,要么找到,如果从根一直到叶子都没找到,那么就是查找失败。

RBNode* rb_search(RBNode* node, int value) {
  
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<iostream>
#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

posted @ 2011-08-27 22:17  xiaodongrush  阅读(2046)  评论(0编辑  收藏  举报