红黑树

在2.6.17的内存管理中,使用了RB TREE来组织page的结构 红黑树 红黑树是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它是在1972年由Rudolf Bayer发明的,他称之为"对称二叉B树",它现代的名字是在 Leo J. Guibas 和 Robert Sedgewick 于1978年写的一篇论文中获得的。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。 红黑树是一种很有意思的平衡检索树。它的统计性能要好于平衡二叉树(有些书籍根据作者姓名,Adelson-Velskii和Landis,将其称为AVL-树),因此,红黑树在很多地方都有应用。在C++ STL中,很多部分(目前包括set, multiset, map, multimap)应用了红黑树的变体(SGI STL中的红黑树有一些变化,这些修改提供了更好的性能,以及对set操作的支持)。 背景和术语 红黑树是一种特定类型的二叉树,它是在计算机科学中用来组织数据比如数字的块的一种结构。所有数据块都存储在节点中。这些节点中的某一个节点总是担当启始位置的功能,它不是任何节点的儿子;我们称之为根节点或根。它有最多两个"儿子",都是它连接到的其他节点。所有这些儿子都可以有自己的儿子,以此类推。这样根节点就有了把它连接到在树中任何其他节点的路径。 如果一个节点没有儿子,我们称之为叶子节点,因为在直觉上它是在树的边缘上。子树是从特定节点可以延伸到的树的某一部分,其自身被当作一个树。在红黑树中,叶子被假定为 null 或空。 由于红黑树也是二叉查找树,它们当中每一个节点都的比较值都必须大于或等于在它的左子树中的所有节点,并且小于或等于在它的右子树中的所有节点。这确保红黑树运作时能够快速的在树中查找给定的值。 用途和好处 红黑树和AVL树一样都对插入时间、删除时间和查找时间提供了最好可能的最坏情况担保。这不只是使它们在时间敏感的应用如即时应用(real time application)中有价值,而且使它们有在提供最坏情况担保的其他数据结构中作为建造板块的价值;例如,在计算几何中使用的很多数据结构都可以基于红黑树。 红黑树在函数式编程中也特别有用,在这里它们是最常用的持久数据结构之一,它们用来构造关联数组和集合,在突变之后它们能保持为以前的版本。除了O(log n)的时间之外,红黑树的持久版本对每次插入或删除需要O(log n)的空间。 红黑树是 2-3-4树的一种等同。换句话说,对于每个 2-3-4 树,都存在至少一个数据元素是同样次序的红黑树。在 2-3-4 树上的插入和删除操作也等同于在红黑树中颜色翻转和旋转。这使得 2-3-4 树成为理解红黑树背后的逻辑的重要工具,这也是很多介绍算法的教科书在红黑树之前介绍 2-3-4 树的原因,尽管 2-3-4 树在实践中不经常使用。 属性 红黑树是每个节点都有颜色特性的二叉查找树,颜色的值是红色或黑色之一。除了二叉查找树带有的一般要求,我们对任何有效的红黑树加以如下增补要求: 1.节点是红色或黑色。 2.根是黑色。 3.所有叶子(外部节点)都是黑色。 4.每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点) 5.从每个叶子到根的所有路径都包含相同数目的黑色节点。 这些约束强制了红黑树的关键属性: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值都要求与树的高度成比例的最坏情况时间,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。 要知道为什么这些特性确保了这个结果,注意到属性4导致了路径不能有两个毗连的红色节点就足够了。最短的可能路径都是黑色节点,最长的可能路径有交替的红色和黑色节点。因为根据属性5所有最长的路径都有相同数目的黑色节点,这就表明了没有路径能多于任何其他路径的两倍长。 在很多树数据结构的表示中,一个节点有可能只有一个儿子,而叶子节点包含数据。用这种范例表示红黑树是可能的,但是这会改变一些属性并使算法复杂。为此,本文中我们使用 "nil 叶子" 或"空(null)叶子",如上图所示,它不包含数据而只充当树在此结束的指示。这些节点在绘图中经常被省略,导致了这些树好象同上述原则相矛盾,而实际上不是这样。与此有关的结论是所有节点都有两个儿子,尽管其中的一个或两个可能是空叶子。 操作 在红黑树上只读操作不需要对用于二叉查找树的操作做出修改,因为它也二叉查找树。但是,在插入和删除之后,红黑属性可能变得违规。恢复红黑属性需要少量(O(log n))的颜色变更(这在实践中是非常快速的)并且不超过三次树旋转(对于插入是两次)。这允许插入和删除保持为 O(log n) 次,但是它导致了非常复杂的操作。 二叉排序树在查找方面提供了很大的方便,但是对worst-case查找/插入/删除/求最值 得时间复杂度都为O(n). 红黑树可以保证在worst-case下查找/插入/删除等的复杂度得到O(lgN)。红黑树保持如下特性: 1。节点不是red 就是black 2。root为black 3。所有的leaf为black 4。所有red node 的孩子为black 5。任一node通过左子树和右子树到达叶子节点的black node个数相同 可以证明 红黑树的最大高度为2lg(N+1),所以在红黑树上的操作的时间复杂度为O(lgN)。 其插入和删除操作的时间复杂度都是O(lgN),插入最多需要2次旋转,删除最多需要3次旋转。 其插入和删除操作可能会与红黑树的特性相违背,所以需要修正。在具体的实现中 1)插入的节点都是red。如果其父节点为red,就违背了条件4 2)如果删除的节点为black,违背了条件5需要修正。3)需要一个NIL来表示叶子节点,共享该叶子节点,且为root的父节点,节省空间。 提供了查找 插入 删除 后继 最值操作。 具体实现: //implement red-black tree /* *xiaopei *06/11/24 */ #ifndef RBTREE_H #define RBTREE_H template struct TreeNode { Type key; int color; TreeNode *parent,*left,*right; }; template class RBTree { private: enum { RED,BLACK }Color; Type key; int color; TreeNode *parent,*left,*right; TreeNode *NIL;// TreeNode *ROOT; void left_rotate(TreeNode *&rotate); void right_rotate(TreeNode *&rotate); void RB_insert_fixup(TreeNode* &node); void RB_delete_fixup(TreeNode* &node); public: //const static TreeNode* nil; RBTree(); TreeNode *successor(const TreeNode *node); TreeNode* insert(Type key); TreeNode* search(Type key); TreeNode* remove(Type key); TreeNode *maxnum(TreeNode*node); TreeNode* minnum(TreeNode*node); TreeNode* getRoot(); void inorder(TreeNode*node); void test() { this->insert(1); this->insert(2); left_rotate(ROOT); } ~RBTree(); }; //template //const TreeNode* RBTree::nil=NIL; template RBTree::RBTree() { NIL=new TreeNode; NIL->color=BLACK; NIL->key=123456; NIL->left=0; NIL->right=0; NIL->parent=0; ROOT=NIL; } template RBTree::~RBTree() { delete NIL; } template TreeNode* RBTree::maxnum(TreeNode*node) { //如果是空树怎么办? TreeNode *max=node; while(max->right!=NIL) max=max->right; return max; } template TreeNode* RBTree::minnum(TreeNode*node) { //如果是空树怎么办? TreeNode *min=node; while(min->left!=NIL) min=min->left; return min; } template TreeNode* RBTree::successor(const TreeNode* node) { if(node->right!=NIL) return minnum(node->right); const TreeNode*s=node; const TreeNode*p=s->parent; while(p!=NIL&&s==p->right){s=p;p=s->parent;} return const_cast*>(p); } template TreeNode* RBTree::getRoot() { TreeNode*root=ROOT; return root; } template void RBTree::left_rotate(TreeNode*& _rotate) { if(_rotate->right==NIL){cout<<"没有右孩子,不能左旋";return ;}//right child is nil, so can not left rotate TreeNode* r = _rotate->right; TreeNode*t,*rotate=_rotate; //deal with r->left rotate->right=r->left; r->left->parent=rotate; //deal with r // t=rotate->parent; // r->parent=t;//rotate 被修改成了NIL 为什么? // rotate=rotate->parent; if(rotate->parent==NIL) {ROOT=r;r->parent=NIL;} else if(rotate==rotate->parent->left) rotate->parent->left=r; else rotate->parent->right=r; r->parent=rotate->parent; //deal with rotate rotate->parent=r; r->left=rotate; } template void RBTree::right_rotate(TreeNode* &_rotate) { TreeNode*rotate=_rotate;//不知道为什么,如果直接对_rotate操作,在过程中会改变_rotate得值 if(rotate->left==NIL)return ;//left child is NIL, so can not right rotate TreeNode* l = rotate->left; //deal with l->right //cout<<"key"<parent->key<right->parent=rotate; rotate->left=l->right; //deal with l l->parent=rotate->parent; if(rotate->parent==NIL) {ROOT=l;l->parent=NIL;} else if(rotate==rotate->parent->left) rotate->parent->left=l; else rotate->parent->right=l; //deal with rotate rotate->parent=l; l->right=rotate; } template TreeNode* RBTree::insert(Type key) { TreeNode *node=new TreeNode; TreeNode *down=ROOT; TreeNode *s=NIL; while(down!=NIL) { s=down; if(keykey) down=down->left; else down=down->right; }//找到合适位置 node->parent=s; if(s==NIL) {ROOT=node;node->parent=NIL;} else if(keykey) s->left=node; else s->right=node; node->key=key; node->left=NIL; node->right=NIL; node->color=RED; RB_insert_fixup(node); return node; } template void RBTree::RB_insert_fixup(TreeNode*& change) { // TreeNode*change=node; TreeNode*father; TreeNode*uncle; while(change->parent->color==RED) { father=change->parent; if(father==father->parent->left) { uncle=father->parent->right;//父节点的兄弟 if(uncle->color==RED)//uncle同样为red { father->color=BLACK; uncle->color=BLACK; father->parent->color=RED; change=father->parent;//进行循环 }else { //cout<<"uncle node color=black"<right)//内插入,左旋 { change=father; //cout<<"before rotate key:"<key<<" color "<color<parent; //cout<<"after rotate key:"<key<<" color "<color<color=BLACK; father->parent->color=RED;//改变颜色 // cout<<"before rotate key:"<key<<" color "<color<parent); // cout<<"before rotate key:"<key<<" color "<color<parent->left;//父节点的兄弟 if(uncle->color==RED)//uncle同样为red { father->color=BLACK; uncle->color=BLACK; father->parent->color=RED; change=father->parent;//进行循环 }else { if(change==father->left)//内插入 { change=father; right_rotate(change); father=change->parent; } //外插入 father->color=BLACK; father->parent->color=RED; left_rotate(father->parent); } } } ROOT->color=BLACK; } template TreeNode* RBTree::search(Type key) { TreeNode *p=ROOT; while(p!=NIL&&p->key!=key) { if(keykey) p=p->left; else p=p->right; } return p; } template TreeNode* RBTree::remove(Type key) { TreeNode *p = this->search(key); if(p==NIL) return p; //rn 为需要改变的节点,找到得key节点,如果只有一个孩子或者没有孩子,则需要被删除的就是该节点,否则(两个孩子) //需要删除的是直接后继节点(首先将key节点用后继节点代替)后继节点没有左孩子 //最后rn最多只有一个孩子 TreeNode *rn=NIL; TreeNode *act; if(p->left==NIL || p->right==NIL) rn=p; else rn = this->successor(p); if(rn->left!=NIL)//如果被删除的节点左孩子不空,则使用act纪录(key节点只有左孩子的情况) act=rn->left; else//act纪录key没有孩子,有右孩子,双子情况 act=rn->right; act->parent=rn->parent;//更新父节点 if(rn->parent==NIL) ROOT=act;//已经将act得parent修改成NIL了 else if(rn == rn->parent->left) rn->parent->left=act; else rn->parent->right=act; if(rn!=p)//key有两个孩子时,实际删除的是p的直接后继rn节点,所以需要将后继节点的信息复制key节点中 p->key=rn->key; if(rn->color==BLACK) RB_delete_fixup(act); return rn; } //删除一个黑色节点后导致两边的bh不同。 template void RBTree::RB_delete_fixup(TreeNode *&_node) { TreeNode*father,*brother,*node; node=_node; father=node->parent; while(node!=ROOT&&node->color==BLACK)//如果color(x)为red,只需要讲color(x)=black就行了 { if(node=father->left)//color(node)=black,father左孩子得black nodes(m-1) =father右孩子的black nodes(m) -1 { brother=father->right; if(brother->color==RED)//color(father)=black,********************case 1 { brother->color=BLACK; father->color=RED; left_rotate(father);//修改了树结构,这一步后brother为node得祖父节点并且brother右孩子的black nodes = m不变 brother=parent->right;//修正node得兄弟节点,现在color(father)=black right(father)=black,并且father->left得 //black nodes还为m-1,father->right得black nodes = m } //如果兄弟为黑,则根据兄弟的孩子来区分,color(Node)=black,color(brother)=black,color(father)未知 if(brother->left->color==BLACK&&brother->right->color==BLACK)//********************case 2 { brother->color=RED;//fater右孩子的black nodes = m-1 node=father;//如果father为red,则循环结束,将father改为black,则整个以father为根的子树左右孩子的black nodes都为m } else { if(brother->right->color==BLACK)//右孩子为黑色********************case 3 { brother->left->color=BLACK; brother->color=RED; right_rotate(brother); brother=father->right;//brother的右孩子为red } //if(brother->right->color==RED)************************case 4 father->color=BLACK; brother->right->color=BLACK;//red修改成black brother->color=father->color; //执行旋转后,brother为node得祖父,brother右孩子得black nodes=m,做孩子的black nodes=m(+father为黑色) left_rotate(father); node=ROOT;//结束 } } } node->color=BLACK; } template void RBTree::inorder(TreeNode*node) { if(node!=NIL) { inorder(node->left); if(node->color==RED) if(node->left->color==RED||node->right->color==RED) cout<<"wrong"<<" "; cout<key<<" "; inorder(node->right); } } #endif

你可能感兴趣的:(红黑树)