本文共 9294 字,大约阅读时间需要 30 分钟。
1、红黑树的性质(参考《算法导论》):
每个节点均有颜色属性,且要么为红色,要么为黑色;
根节点为黑色;
红色节点的子节点不可以为红色
对每个节点,从该节点到期子孙节点的所有路径上包含相同数目的黑节点
2、红黑树节点的定义:
template class RBTNode{ private: T data; char color; RBTNode *lchild; RBTNode *rchild; RBTNode *parent; private: RBTNode() : lchild(0), rchild(0), parent(0), color('R') {} RBTNode(const T& elem) : data(elem), lchild(0), rchild(0), parent(0), color('R') {} private: template friend class RBTree; // 红黑树 template friend class RBTreeOperator; // 另一个类,仅用于测试,可以对红黑树进行遍历};
3、红黑树类的定义:
template class RBTree{ public: RBTree():root(0){} ~RBTree(){} void insert(const T& elem); // 插入元素 void erase(const T& elem); // 删除元素 bool find(const T& elem); // 查找元素 void clear(); // 清除节点 private: typedef RBTNode * pointer; private: pointer left_rotate(pointer p); // 节点左旋 pointer right_rotate(pointer p); // 节点右旋 pointer find_insert(const T& elem); // 定位待插入元素的父节点 pointer find_del(const T& elem); // 定位待删除元素 pointer find_minimum(pointer p); // 定位以p为根的子树的最小元素 pointer find_successor(pointer p); // 定位中序遍历时节点p的后继元素,同find_minimum用于定位实际删除的节点 void clear_node(pointer p); // 删除节点p private: pointer root; private: template friend class RBTreeOperator; // 同上};
// 以下为类成员函数的实现
template RBTNode * RBTree ::left_rotate(RBTNode *p) // 节点左旋,返回子树的新根节点{ if(!p || !p->rchild) { return p; } RBTNode *q = p->rchild; p->rchild = q->lchild; if(p->rchild) { p->rchild->parent = p; } q->lchild = p; q->parent = p->parent; p->parent = q; if(!q->parent) { root = q; } else { if(p == q->parent->lchild) { q->parent->lchild = q; } else { q->parent->rchild = q; } } return q;}template RBTNode * RBTree ::right_rotate(RBTNode *p){ if(!p || !p->lchild) { return p; } RBTNode *q = p->lchild; p->lchild = q->rchild; if(p->lchild) { p->lchild->parent = p; } q->rchild = p; q->parent = p->parent; p->parent = q; if(!q->parent) { root = q; } else { if(p == q->parent->lchild) { q->parent->lchild = q; } else { q->parent->rchild = q; } } return q;}template RBTNode * RBTree ::find_insert(const T& elem){ RBTNode *p(root), *q(root); while(q) { p = q; q = p->data < elem ? p->rchild : p->lchild; } return p;}template RBTNode * RBTree ::find_del(const T& elem){ RBTNode *p = root; while(p) { if(p->data == elem) { break; } if(p->data < elem) { p = p->rchild; } else { p = p->lchild; } } return p;}template RBTNode * RBTree ::find_minimum(RBTNode *p){ while(p && p->lchild) { p = p->lchild; } return p;}template RBTNode * RBTree ::find_successor(RBTNode *p) // 查找p于中序遍历中的后继结点{ if(!p) { return p; } if(p->rchild) { return find_minimum(p->rchild); } else // 无右子节点,则向上寻找第一个为其父节点的左孩子的节点q,则q的父节点即为p的后继 { RBTNode *q = p->parent; while(q && p == q->rchild) { p = q; q = q->parent; } return q; }}template void RBTree ::clear_node(RBTNode *node){ if(node) { clear_node(node->lchild); clear_node(node->rchild); delete node; }}template void RBTree ::insert(const T& elem){ RBTNode *p(new RBTNode (elem)), *q(find_insert(elem)), *r(0); p->parent = q; if(!q) // 树根为空,则令新插入节点p为树根 { root = p; root->color = 'B'; return; } if(q->data < elem) { q->rchild = p; } else { q->lchild = p; } while(q && q != root && q->color == 'R') //p与其父节点q的颜色均为红色,违反定义 { RBTNode *node = q->parent; if(q == node->lchild) // 若q为其父节点的左孩子 { r = node->rchild; // r为p的叔父 if(r && r->color == 'R') // 若q与r的颜色均为红,则将二者颜色变黑,其父节点变红,并将p指向该父节点,下一步循环 { r->color = 'B'; q->color = 'B'; node->color = 'R'; p = node; q = p->parent;
continue;
}
if(p == q->rchild) // 若p为q的右孩子,则应将q左旋并交换地址,使得p为q的左孩子 { left_rotate(q); p = q; q = p->parent; } right_rotate(node); // 通过将node节点右旋以及node与q的颜色交换,使该子树根节点为黑色,同时消除了红-红父子冲突 q->color = 'B'; node->color = 'R'; } else { r = node->lchild; if(r && r->color == 'R') { r->color = 'B'; q->color = 'B'; node->color = 'R'; p = node; q = p->parent;
continue; } else { if(p == q->lchild) { right_rotate(q); p = q; q = p->parent; } left_rotate(node); q->color = 'B'; node->color = 'R'; } } } root->color = 'B'; // 根节点染红}template void RBTree ::erase(const T& elem){ RBTNode *q(find_del(elem)), *p(0), *r(0), *node(0); if(!q) { return; } if(!q->lchild || !q->rchild) // q孩子数少于2,则直接删除q即可 { node = q; } else // 否则删除其后继位置的节点node,node必然不会同时有左右两个孩子 { node = find_successor(q); } if(node != q) // 将node的值赋值到q { q->data = node->data; }
// 以下代码处理被删除节点为根节点或者无子节点的情况 q = node->parent; p = (node->lchild ? node->lchild : node->rchild); if(q) { if(node == q->lchild) { q->lchild = p; } else { q->rchild = p; } } else { root = p; } if(!p) { delete node; return; } else { p->parent = q; } if(node->color == 'B') // 若被删除节点为黑色,则其所在的路径上少了一个黑色节点,需要矫正 { while(p != root && p->color == 'B') // 循环条件:还未遇到红色节点以染成黑色 { q = p->parent; if(p == q->lchild) // 若p为其父节点q的左孩子 { r = q->rchild; // r为q的右孩子,p的兄弟节点 if(!r) // q只有一个孩子,无需处理该子树,p上移一层 { p = q; continue; } if(r->color == 'R') // 若r为红色,则通过q的左旋以及r与q的颜色交换,转换为p的兄弟为黑色的情况(后边处理该情况) { r->color = 'B'; q->color = 'R'; left_rotate(q); r = q->rchild;}
// 以下为p的兄弟节点r为黑色的情况,分三种情况处理:
if((!r->lchild ||r->lchild->color == 'B') && (!r->rchild || r->rchild->color == 'B')) // 1)r的子节点均不为红色,则将r染成 红色,使得q向下缺少一层黑色,故可将p指向q,上移 { r->color = 'R'; p = q; } else { if(!r->rchild || r->rchild->color == 'B') // 2)r的右子节点为空或黑色,通过旋转使p的兄弟右孩子为红色 { r->color = 'R'; r->lchild->color = 'B'; r = right_rotate(r); } r->color = q->color; // 3)r的右子节点为红色,将q左旋并通过颜色交换,使得右子树的一个黑色为左右共享,左子树不再缺少 黑色,结束循环即可 q->color = 'B'; r->rchild->color = 'B'; left_rotate(q); p = root; } } else // if代码的镜像 { r = q->lchild; if(!r) { p = q; continue; } if(r->color == 'R') { r->color = 'B'; q->color = 'R'; right_rotate(q); r = q->lchild; } if((!r->lchild || r->lchild->color == 'B') && (!r->rchild || r->rchild->color == 'B')) { r->color = 'R'; p = q; } else { if(!r->lchild || r->lchild->color == 'B') { r->color = 'R'; r->rchild->color = 'B'; r = left_rotate(r); } r->color = q->color; q->color = 'B'; r->lchild->color = 'B'; right_rotate(q); p = root; } } } p->color = 'B'; // 找到了可以弥补的节点,将其染成黑色 } delete node; //删除node即可}template bool RBTree ::find(const T& elem){ return find_del(elem) != 0;}template void RBTree ::clear(){ clear_node(root); root = 0;}
4、 树的遍历: template class RBTreeOperator{ public: RBTreeOperator() : tree(0) {} RBTreeOperator(RBTree & t) : tree(&t){} ~RBTreeOperator(){} void FirstOrderTree(); void InOrderTree(); void PostOrderTree(); void LevelOrderTree(); private: void FTraverse(RBTNode *node); void ITraverse(RBTNode *node); void PTraverse(RBTNode *node); void LTraverse(RBTNode *node); private: RBTree *tree;};
// 以下为成员函数的实现
template void RBTreeOperator ::FTraverse(RBTNode *node){ stack *> s; s.push(node); if(!node) { return; } while(!s.empty()) { node = s.top(); s.pop(); cout << node->data << "\t"; if(node->rchild) { s.push(node->rchild); } if(node->lchild) { s.push(node->lchild); } }}template void RBTreeOperator ::ITraverse(RBTNode *node){ stack *> s; while(node) { s.push(node); node = node->lchild; } while(!s.empty()) { node = s.top(); cout << node->data << "\t"; s.pop(); RBTNode *temp = node->rchild; while(temp) { s.push(temp); temp = temp->lchild; } }}template void RBTreeOperator ::PTraverse(RBTNode *node){ stack *> s; RBTNode *p(node), *q(0); while(p) { s.push(p); p = p->lchild; } while(!s.empty()) { p = s.top(); if(p->rchild && q != p->rchild) { p = p->rchild; while(p) { s.push(p); p = p->lchild; } } else { q = p; cout << p->data << "\t"; s.pop(); } }}template void RBTreeOperator ::LTraverse(RBTNode *node){ queue *> q; if(!node) { return; } q.push(node); while(!q.empty()) { node = q.front(); if(node->lchild) { q.push(node->lchild); } if(node->rchild) { q.push(node->rchild); } cout << node->data << "\t"; q.pop(); }}template void RBTreeOperator ::FirstOrderTree(){ FTraverse(tree->root); cout << endl;}template void RBTreeOperator ::InOrderTree(){ ITraverse(tree->root); cout << endl;}template void RBTreeOperator ::PostOrderTree(){ PTraverse(tree->root); cout << endl;}template void RBTreeOperator ::LevelOrderTree(){ LTraverse(tree->root); cout << endl;}
5、测试: int main(int argc, char** argv) { RBTree tree; RBTreeOperator op(tree); const int N(10); int p[N] = {2, 5, 3, 9, 7, 8, 6, 10, 1, 4}; for(int i = 0; i < N; ++i) { tree.insert(p[i]); } op.FirstOrderTree(); op.InOrderTree(); op.PostOrderTree(); op.LevelOrderTree(); for(int i = 0; i < N; ++i) { tree.erase(p[i]); op.FirstOrderTree(); op.InOrderTree(); } return 0;}
6、小结: 以上便是今天下午的劳动成果了。写完代码后感觉对红黑树的理解更深入一些了,更重要的是对节点旋转的作用以及需要节点旋转的场合有了更深的了解:通过旋转可以使一个节点的左右两颗子树“互通有无”,弥补其中一棵子树缺少的颜色,这是通过对另一棵子树的某个节点的颜色进行共享实现的。另一收获就是码代码的时候万万要小心,一个不细心导致的错误,将会让你付出很大的代价去发现它!
转载地址:http://jyzob.baihongyu.com/