深入理解STL之RBTree重点讲义资料.docx
- 文档编号:5461667
- 上传时间:2022-12-16
- 格式:DOCX
- 页数:30
- 大小:599.30KB
深入理解STL之RBTree重点讲义资料.docx
《深入理解STL之RBTree重点讲义资料.docx》由会员分享,可在线阅读,更多相关《深入理解STL之RBTree重点讲义资料.docx(30页珍藏版)》请在冰豆网上搜索。
深入理解STL之RBTree重点讲义资料
深入理解STL之RBTree
最近一直忙于校招的笔试,STL的深入理解系列也耽搁了好几天,再加上!
红黑树真的是超级超级难理解,超级超级复杂,参考了好多博客上的大神的理解才稍微明白一点,勉强入个门,下面请以一个菜鸟的角度跟着我一起学习STL的红黑树吧。
概述
红黑树是平衡二叉搜索树的一种,其通过特定的操作来保持二叉查找树的平衡。
首先,我们来复习一下二叉查找树的知识,建议如果对二叉查找树不理解的先去搜一下相关博客来了解一下。
二叉搜索树是指一个空树或者具有以下性质的二叉树:
∙任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
∙任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
∙任意节点的左、右子树也分别为二叉查找树;
∙没有键值相等的节点
我们知道,一颗由n个节点随机构造的二叉搜索树的高度为logn,但是,由于输入值往往不够随机,导致二叉搜索树可能失去平衡,造成搜索效率低下的情况。
从而,引出了平衡二叉搜索树的概念。
对于“平衡”这个约束不同的结构有不同的规定,如AVL树要求任何节点的两个子树的高度最大差别为1,可谓是高度平衡啊;而红黑树仅仅确保没有一条路径会比其他路径长出两倍,因而达到接近平衡的目的。
红黑数不仅是一个平衡二叉搜索树,而且还定义了相当多的约束来确保插入和删除等操作后能达到平衡。
那么,红黑树究竟是怎么定义,来使得能够达到平衡的目的呢?
我们接着看下去。
红黑树的定义
红黑树既然属于二叉搜索树的一种,当然需要满足上述二叉搜索树的性质,除此之外,红黑树还为每一个节点增加了一个存储位来表示节点的颜色属性,它可以为red或者black,通过对任何一条从根到叶子节点的路径上每个点进行着色方式的限制,来确保没有一条路径会比其他路径长出两倍,因而达到接近平衡的目的。
那么,红黑树是如何进行着色的呢?
下面引出了红黑树的五条性质:
∙每个节点或者是黑色,或者是红色。
∙根节点是黑色。
∙每个叶子节点(NIL)是黑色。
[注意:
这里叶子节点,是指为空(NIL或NULL)的叶子节点!
]
∙如果一个节点是红色的,则它的子节点必须是黑色的。
∙从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
正是这五条性质,使得红黑树的高度能保持在logn,从而达到平衡的目的,进而使得其在查找、插入和删除的时间复杂度最坏为O(logn),下面就是一棵典型的红黑树。
注:
因本人能力有限可能无法将红黑树讲得很清楚全面,而且STL红黑树的实现也较为复杂,建议先到下面推荐的几篇博客里去补补知识
∙教你初步了解红黑树
∙【数据结构和算法05】红-黑树(看完包懂~)
∙一步一图一代码,一定要让你真正彻底明白红黑树
红黑树的节点结构
红黑树的节点在二叉树的节点结构上增加了颜色属性,而且,为了更好的进行插入和删除操作,进而增加了指向父节点的指针。
为了更好的弹性,STL红黑树的节点采用双层设计,将不依赖模板的参数提取出来,作为base结构,然后用带模板的节点结构取继承它。
下面是红黑树节点结构的源码:
typedefbool__rb_tree_color_type;
const__rb_tree_color_type__rb_tree_red=false;//紅色為0
const__rb_tree_color_type__rb_tree_black=true;//黑色為1
struct__rb_tree_node_base
{
typedef__rb_tree_color_typecolor_type;
typedef__rb_tree_node_base*base_ptr;
color_typecolor;//节点颜色
base_ptrparent;//指向父节点
base_ptrleft;//指向左子节点
base_ptrright;//指向右子节点
//一直往左走,就能找到红黑树的最小值节点
//二叉搜索树的性质
staticbase_ptrminimum(base_ptrx)
{
while(x->left!
=0)x=x->left;
returnx;
}
//一直往右走,就能找到红黑树的最大值节点
//二叉搜索树的性质
staticbase_ptrmaximum(base_ptrx)
{
while(x->right!
=0)x=x->right;
returnx;
}
};
//真正的节点定义,采用双层节点结构
//基类中不包含模板参数
template
struct__rb_tree_node:
public__rb_tree_node_base
{
typedef__rb_tree_node
Valuevalue_field;//節點實值
};红黑树的迭代器
为了将RBtree实现为一个泛型容器,迭代器的设计很关键。
我们要考虑它的型别,以及前进(increment)、后退(devrement)、提领(dereference)和成员访问(memberaccess)等操作。
迭代器和节点一样,采用双层设计,STL红黑树的节点__rb_tree_node继承于__rb_tree_node_base;STL的迭代器结构__rb_tree_iterator继承于__rb_tree_base_iterator,我们可以用一张图来解释这样的设计目的。
将这些分开设计,可以保证对节点和迭代器的操作更具有弹性。
下面来看迭代器的源码:
struct__rb_tree_base_iterator
{
typedef__rb_tree_node_base:
:
base_ptrbase_ptr;
typedefbidirectional_iterator_tagiterator_category;
typedefptrdiff_tdifference_type;
base_ptrnode;//用来连接红黑树的节点
//寻找该节点的后继节点上
voidincrement()
{
if(node->right!
=0){//如果存在右子节点
node=node->right;//直接跳到右子节点上
while(node->left!
=0)//然后一直往左子树走,直到左子树为空
node=node->left;
}
else{//没有右子节点
base_ptry=node->parent;//找出父节点
while(node==y->right){//如果该节点一直为它的父节点的右子节点
node=y;//就一直往上找,直到不为右子节点为止
y=y->parent;
}
if(node->right!
=y)//若此时该节点不为它的父节点的右子节点
node=y;//此时的父节点即为要找的后继节点
//否则此时的node即为要找的后继节点,此为特殊情况,如下
//我们要寻找根节点的下一个节点,而根节点没有右子节点
//此种情况需要配合rbtree的header节点的特殊设计,后面会讲到
}
}
//寻找该节点你的前置节点
voiddecrement()
{
if(node->color==__rb_tree_red&&//如果此节点是红节点
node->parent->parent==node)//且父节点的父节点等于自己
node=node->right;//则其右子节点即为其前置节点
//以上情况发生在node为header时,即node为end()时
//注意:
header的右子节点为mostright,指向整棵树的max节点,后面会有解释
elseif(node->left!
=0){//如果存在左子节点
base_ptry=node->left;//跳到左子节点上
while(y->right!
=0)//然后一直往右找,知道右子树为空
y=y->right;
node=y;//则找到前置节点
}
else{//如果该节点不存在左子节点
base_ptry=node->parent;//跳到它的父节点上
while(node==y->left){//如果它等于它的父子节点的左子节点
node=y;//则一直往上查找
y=y->parent;
}//直到它不为父节点的左子节点未知
node=y;//此时他的父节点即为要找的前置节点
}
}
}
template
struct__rb_tree_iterator:
public__rb_tree_base_iterator
{
//配合迭代器萃取机制的一些声明
typedefValuevalue_type;
typedefRefreference;
typedefPtrpointer;
typedef__rb_tree_iterator
typedef__rb_tree_iterator
typedef__rb_tree_iterator
typedef__rb_tree_node
//迭代器的构造函数
__rb_tree_iterator(){}
__rb_tree_iterator(link_typex){node=x;}
__rb_tree_iterator(constiterator&it){node=it.node;}
//提领和成员访问函数,重载了*和->操作符
referenceoperator*()const{returnlink_type(node)->value_field;}
pointeroperator->()const{return&(operator*());}
//前置++和后置++
self&operator++(){increment();return*this;}
selfoperator++(int){
selftmp=*this;
increment();//直接调用increment函数
returntmp;
}
//前置--和后置--
self&operator--(){decrement();return*this;}
selfoperator--(int){
selftmp=*this;
decrement();//直接调用decrement函数
returntmp;
}
};
在上述源代码中,一直提到STLRBTree特殊节点header的设计,这个会在RBTree结构中讲到,下面跟着我一起继续往下看吧。
红黑树的数据结构
有了上面的节点和迭代器设计,就能很好的定义出一颗RBTree了。
废话不多说,一步一步来剖析源代码吧。
template classAlloc=alloc> classrb_tree{ protected: typedefvoid*void_pointer; typedef__rb_tree_node_base*base_ptr; typedef__rb_tree_node typedefsimple_alloc typedef__rb_tree_color_typecolor_type; public: //一些类型声明 typedefKeykey_type; typedefValuevalue_type; typedefvalue_type*pointer; typedefconstvalue_type*const_pointer; typedefvalue_type&reference; typedefconstvalue_type&const_reference; typedefrb_tree_node*link_type; typedefsize_tsize_type; typedefptrdiff_tdifference_type; protected: //RB-tree的数据结构 size_typenode_count;//记录树的节点个数 link_typeheader;//header节点设计 Comparekey_compare;//节点间的键值大小比较准则 //以下三个函数用来取得header的成员 link_type&root()const{return(link_type&)header->parent;} link_type&leftmost()const{return(link_type&)header->left;} link_type&rightmost()const{return(link_type&)header->right;} //以下六个函数用来取得节点的成员 staticlink_type&left(link_typex){return(link_type&)(x->left);} staticlink_type&right(link_typex){return(link_type&)(x->right);} staticlink_type&parent(link_typex){return(link_type&)(x->parent);} staticreferencevalue(link_typex){returnx->value_field;} staticconstKey&key(link_typex){returnKeyOfValue()(value(x));} staticcolor_type&color(link_typex){return(color_type&)(x->color);} //以下六个函数用来取得节点的成员,由于双层设计,导致这里需要两个定义 staticlink_type&left(base_ptrx){return(link_type&)(x->left);} staticlink_type&right(base_ptrx){return(link_type&)(x->right);} staticlink_type&parent(base_ptrx){return(link_type&)(x->parent);} staticreferencevalue(base_ptrx){return((link_type)x)->value_field;} staticconstKey&key(base_ptrx){returnKeyOfValue()(value(link_type(x)));} staticcolor_type&color(base_ptrx){return(color_type&)(link_type(x)->color);} //求取极大值和极小值,这里直接调用节点结构的函数极可 staticlink_typeminimum(link_typex){ return(link_type)__rb_tree_node_base: : minimum(x); } staticlink_typemaximum(link_typex){ return(link_type)__rb_tree_node_base: : maximum(x); } public: //RBTree的迭代器定义 typedef__rb_tree_iterator typedef__rb_tree_iterator const_iterator; public: Comparekey_comp()const{returnkey_compare;}//由于红黑树自带排序功能,所以必须传入一个比较器函数 iteratorbegin(){returnleftmost();}//RBTree的起始节点为左边最小值节点 const_iteratorbegin()const{returnleftmost();} iteratorend(){returnheader;}//RBTree的终止节点为右边最大值节点 const_iteratorend()const{returnheader;} boolempty()const{returnnode_count==0;}//判断红黑树是否为空 size_typesize()const{returnnode_count;}//获取红黑树的节点个数 size_typemax_size()const{returnsize_type(-1);}//获取红黑树的最大节点个数, //没有容量的概念,故为sizetype最大值 }; 我们看到,在RBTree的数据结构中,定义了RBTree节点和迭代器,然后添加了header节点,以及node_count参数,其他都是一下简单的函数声明和类型声明。 除此之外,并没有过多的增加东西。 这里理解起来还是比较简单,至于header有什么作用,请继续往下看。 红黑树的构造与内存管理 红黑树的构造函数 红黑树的空构造函数将创建一个空树,此”空树“非彼二叉树的空树也。 空构造函数首先配置一个节点空间,使header指向该节点空间,然后将header的leftmost和rightmost指向自己,父节点指向0。 非空的STLRBTree中,header和root之间互为父节点,然后header的leftmost始终指向该树的最小值节点,rightmost始终指向该树的最大值节点,其示例图如下(左图为空树,右图为非空树): 下面来看看它的构造函数代码吧: template classAlloc=alloc> classrb_tree{ //这部分代码是从红黑树的结构定义中提取出来的 protected: typedefsimple_alloc link_typeget_node(){returnrb_tree_node_allocator: : allocate();}//配置空间 link_typecreate_node(constvalue_type&x){ link_typetmp=get_node();//配置空間 __STL_TRY{ construct(&tmp->value_field,x);//构造内容 } __STL_UNWIND(put_node(tmp)); returntmp; } //初始化函数,用来初始化一棵RBTree voidinit(){ header=get_node();//产生一个节点空间,令header指向它 color(header)=__rb_tree_red;//令header为红色,用来区分header和root root()=0; leftmost()=header;//令header的左子节点为其自己 rightmost()=header;//令header的右子节点为其自己 } //真正的默认构造函数 rb_tree(constCompare&comp=Compare()) : node_count(0),key_compare(comp){init();}//直接调用初始化函数 //带参构造函数,以另一棵RBTree为初值来初始化 rb_tree(constrb_tree : node_count(0),key_compare(x.key_compare) { header=get_node();//產生一個節點空間,令header指向它 color(header)=__rb_tree_red;//令header為紅色 if(x.root()==0){//如果x是個空白樹 root()=0; leftmost()=header;//令header的左子節點為自己。 rightmost()=header;//令header的右子節點為自己。 } else{//x不是一個空白樹 __STL_TRY{ root()=__copy(x.root(),header);//调用copy函数 } __STL_UNWIND(put_node(header)); leftmost()=minimum(root());//令header的左子節點為最小節點 rightmost()=maximum(root());//令header的右子節點為最大節點 } node_count=x.node_count; } //copy函数定义
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 深入 理解 STL RBTree 重点 讲义 资料