红黑树深入剖析及Java实现.docx
- 文档编号:4371815
- 上传时间:2023-05-07
- 格式:DOCX
- 页数:32
- 大小:365.36KB
红黑树深入剖析及Java实现.docx
《红黑树深入剖析及Java实现.docx》由会员分享,可在线阅读,更多相关《红黑树深入剖析及Java实现.docx(32页珍藏版)》请在冰点文库上搜索。
红黑树深入剖析及Java实现
红黑树深入剖析及Java实现
概述
红黑树(RedBlackTree)是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。
它是在1972年由RudolfBayer发明的,当时被称为平衡二叉B树(symmetricbinaryB-trees)。
红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。
二叉查找树(BST)
二叉查找树(BinarySearchTree,简称BST)是一棵二叉树,它的左子节点的值比父节点的值要小,右节点的值要比父节点的值大。
它的高度决定了它的查找效率。
在理想的情况下,二叉查找树增删查改的时间复杂度为O(logN)(其中N为节点数),最坏的情况下为O(N)。
当它的高度为logN+1时,我们就说二叉查找树是平衡的。
常见的BST:
BST的查找操作
Tkey=asearchkey
Noderoot=pointtotherootofaBST
while(true){
if(root==null){
break;
}
if(root.value.equals(key)){
returnroot;
}
elseif(pareTo(root.value)<0){
root=root.left;
}
else{
root=root.right;
}
}
returnnull;
说明:
当BST查找的时候,先与当前节点进行比较,直到当前节点指针为空或者查找到对应的节点,程序查找结束。
-如果相等的话就返回当前节点;
-如果少于当前节点则继续查找当前节点的左节点;
-如果大于当前节点则继续查找当前节点的右节点。
BST的插入操作
Nodenode=createanewnodewithspecifyvalue
Noderoot=pointtherootnodeofaBST
Nodeparent=null;
//findtheparentnodetoappendthenewnode
while(true){
if(root==null)break;
parent=root;
if(pareTo(root.value)<=0){
root=root.left;
}else{
root=root.right;
}
}
if(parent!
=null){
if(pareTo(parent.value)<=0){//appendtoleft
parent.left=node;
}else{//appendtoright
parent.right=node;
}
}
插入操作先通过循环查找到待插入的节点的父节点,和查找父节点的逻辑一样,都是比大小,小的往左,大的往右。
找到父节点后,对比父节点,小的就插入到父节点的左节点,大就插入到父节点的右节点上。
BST的删除操作
删除操作的步骤如下:
查找到要删除的节点。
如果待删除的节点是叶子节点,则直接删除。
如果待删除的节点不是叶子节点,则先找到待删除节点的中序遍历的后继节点,用该后继节点的值替换待删除的节点的值,然后删除后继节点。
BST存在的问题
BST存在的主要问题是,数在插入的时候会导致树倾斜,不同的插入顺序会导致树的高度不一样,而树的高度直接的影响了树的查找效率。
理想的高度是logN,最坏的情况是所有的节点都在一条斜线上,这样的树的高度为N。
RBTree
基于BST存在的问题,一种新的树——平衡二叉查找树(BalancedBST)产生了。
平衡树在插入和删除的时候,会通过旋转操作将高度保持在logN。
其中两款具有代表性的平衡树分别为AVL树和红黑树。
AVL树由于实现比较复杂,而且插入和删除性能差,在实际环境下的应用不如红黑树。
红黑树(Red-BlackTree,以下简称RBTree)的实际应用非常广泛,比如Linux内核中的完全公平调度器、高精度计时器、ext3文件系统等等,各种语言的函数库如Java的TreeMap和TreeSet,C++STL的map、multimap、multiset等。
RBTree也是函数式语言中最常用的持久数据结构之一,在计算几何中也有重要作用。
值得一提的是,Java8中HashMap的实现也因为用RBTree取代链表,性能有所提升。
RBTree的定义
RBTree的定义如下:
任何一个节点都有颜色,黑色或者红色
根节点是黑色的
父子节点之间不能出现两个连续的红节点
任何一个节点向下遍历到其子孙的叶子节点,所经过的黑节点个数必须相等
空节点被认为是黑色的
用数据结构表示如下:
classNode
publicTvalue;
publicNode
publicbooleanisRed;
publicNode
publicNode
}
RBTree在理论上还是一棵BST树,但是它在对BST的插入和删除操作时会维持树的平衡,即保证树的高度在[logN,logN+1](理论上,极端的情况下可以出现RBTree的高度达到2*logN,但实际上很难遇到)。
这样RBTree的查找时间复杂度始终保持在O(logN)从而接近于理想的BST。
RBTree的删除和插入操作的时间复杂度也是O(logN)。
RBTree的查找操作就是BST的查找操作。
RBTree的旋转操作
旋转操作(Rotate)的目的是使节点颜色符合定义,让RBTree的高度达到平衡。
Rotate分为left-rotate(左旋)和right-rotate(右旋),区分左旋和右旋的方法是:
待旋转的节点从左边上升到父节点就是右旋,待旋转的节点从右边上升到父节点就是左旋。
RBTree的查找操作
RBTree的查找操作和BST的查找操作是一样的。
RBTree的插入操作
RBTree的插入与BST的插入方式是一致的,只不过是在插入过后,可能会导致树的不平衡,这时就需要对树进行旋转操作和颜色修复(在这里简称插入修复),使得它符合RBTree的定义。
新插入的节点是红色的,插入修复操作如果遇到父节点的颜色为黑则修复操作结束。
也就是说,只有在父节点为红色节点的时候是需要插入修复操作的。
插入修复操作分为以下的三种情况,而且新插入的节点的父节点都是红色的:
叔叔节点也为红色。
叔叔节点为空,且祖父节点、父节点和新节点处于一条斜线上。
叔叔节点为空,且祖父节点、父节点和新节点不处于一条斜线上。
插入操作-case1
case1的操作是将父节点和叔叔节点与祖父节点的颜色互换,这样就符合了RBTRee的定义。
即维持了高度的平衡,修复后颜色也符合RBTree定义的第三条和第四条。
下图中,操作完成后A节点变成了新的节点。
如果A节点的父节点不是黑色的话,则继续做修复操作。
插入操作-case2
case2的操作是将B节点进行右旋操作,并且和父节点A互换颜色。
通过该修复操作RBTRee的高度和颜色都符合红黑树的定义。
如果B和C节点都是右节点的话,只要将操作变成左旋就可以了。
插入操作-case3
case3的操作是将C节点进行左旋,这样就从case3转换成case2了,然后针对case2进行操作处理就行了。
case2操作做了一个右旋操作和颜色互换来达到目的。
如果树的结构是下图的镜像结构,则只需要将对应的左旋变成右旋,右旋变成左旋即可。
插入操作的总结
插入后的修复操作是一个向root节点回溯的操作,一旦牵涉的节点都符合了红黑树的定义,修复操作结束。
之所以会向上回溯是由于case1操作会将父节点,叔叔节点和祖父节点进行换颜色,有可能会导致祖父节点不平衡(红黑树定义3)。
这个时候需要对祖父节点为起点进行调节(向上回溯)。
祖父节点调节后如果还是遇到它的祖父颜色问题,操作就会继续向上回溯,直到root节点为止,根据定义root节点永远是黑色的。
在向上的追溯的过程中,针对插入的3中情况进行调节。
直到符合红黑树的定义为止。
直到牵涉的节点都符合了红黑树的定义,修复操作结束。
RBTree的删除操作
删除操作首先需要做的也是BST的删除操作,删除操作会删除对应的节点,如果是叶子节点就直接删除,如果是非叶子节点,会用对应的中序遍历的后继节点来顶替要删除节点的位置。
删除后就需要做删除修复操作,使的树符合红黑树的定义,符合定义的红黑树高度是平衡的。
删除修复操作在遇到被删除的节点是红色节点或者到达root节点时,修复操作完毕。
删除修复操作是针对删除黑色节点才有的,当黑色节点被删除后会让整个树不符合RBTree的定义的第四条。
需要做的处理是从兄弟节点上借调黑色的节点过来,如果兄弟节点没有黑节点可以借调的话,就只能往上追溯,将每一级的黑节点数减去一个,使得整棵树符合红黑树的定义。
删除操作的总体思想是从兄弟节点借调黑色节点使树保持局部的平衡,如果局部的平衡达到了,就看整体的树是否是平衡的,如果不平衡就接着向上追溯调整。
删除修复操作分为四种情况(删除黑节点后):
待删除的节点的兄弟节点是红色的节点。
待删除的节点的兄弟节点是黑色的节点,且兄弟节点的子节点都是黑色的。
待调整的节点的兄弟节点是黑色的节点,且兄弟节点的左子节点是红色的,右节点是黑色的(兄弟节点在右边),如果兄弟节点在左边的话,就是兄弟节点的右子节点是红色的,左节点是黑色的。
待调整的节点的兄弟节点是黑色的节点,且右子节点是是红色的(兄弟节点在右边),如果兄弟节点在左边,则就是对应的就是左节点是红色的。
删除操作-case1
由于兄弟节点是红色节点的时候,无法借调黑节点,所以需要将兄弟节点提升到父节点,由于兄弟节点是红色的,根据RBTree的定义,兄弟节点的子节点是黑色的,就可以从它的子节点借调了。
case1这样转换之后就会变成后面的case2,case3,或者case4进行处理了。
上升操作需要对C做一个左旋操作,如果是镜像结构的树只需要做对应的右旋操作即可。
之所以要做case1操作是因为兄弟节点是红色的,无法借到一个黑节点来填补删除的黑节点。
删除操作-case2
case2的删除操作是由于兄弟节点可以消除一个黑色节点,因为兄弟节点和兄弟节点的子节点都是黑色的,所以可以将兄弟节点变红,这样就可以保证树的局部的颜色符合定义了。
这个时候需要将父节点A变成新的节点,继续向上调整,直到整颗树的颜色符合RBTree的定义为止。
case2这种情况下之所以要将兄弟节点变红,是因为如果把兄弟节点借调过来,会导致兄弟的结构不符合RBTree的定义,这样的情况下只能是将兄弟节点也变成红色来达到颜色的平衡。
当将兄弟节点也变红之后,达到了局部的平衡了,但是对于祖父节点来说是不符合定义4的。
这样就需要回溯到父节点,接着进行修复操作。
删除操作-case3
case3的删除操作是一个中间步骤,它的目的是将左边的红色节点借调过来,这样就可以转换成case4状态了,在case4状态下可以将D,E节点都阶段过来,通过将两个节点变成黑色来保证红黑树的整体平衡。
之所以说case-3是一个中间状态,是因为根据红黑树的定义来说,下图并不是平衡的,他是通过case2操作完后向上回溯出现的状态。
之所以会出现case3和后面的case4的情况,是因为可以通过借用侄子节点的红色,变成黑色来符合红黑树定义4.
删除操作-case4
Case4的操作是真正的节点借调操作,通过将兄弟节点以及兄弟节点的右节点借调过来,并将兄弟节点的右子节点变成红色来达到借调两个黑节点的目的,这样的话,整棵树还是符合RBTree的定义的。
Case4这种情况的发生只有在待删除的节点的兄弟节点为黑,且子节点不全部为黑,才有可能借调到两个节点来做黑节点使用,从而保持整棵树都符合红黑树的定义。
删除操作的总结
红黑树的删除操作是最复杂的操作,复杂的地方就在于当删除了黑色节点的时候,如何从兄弟节点去借调节点,以保证树的颜色符合定义。
由于红色的兄弟节点是没法借调出黑节点的,这样只能通过选择操作让他上升到父节点,而由于它是红节点,所以它的子节点就是黑的,可以借调。
对于兄弟节点是黑色节点的可以分成3种情况来处理,当所以的兄弟节点的子节点都是黑色节点时,可以直接将兄弟节点变红,这样局部的红黑树颜色是符合定义的。
但是整颗树不一定是符合红黑树定义的,需要往上追溯继续调整。
对于兄弟节点的子节点为左红右黑或者(全部为红,右红左黑)这两种情况,可以先将前面的情况通过选择转换为后一种情况,在后一种情况下,因为兄弟节点为黑,兄弟节点的右节点为红,可以借调出两个节点出来做黑节点,这样就可以保证删除了黑节点,整棵树还是符合红黑树的定义的,因为黑色节点的个数没有改变。
红黑树的删除操作是遇到删除的节点为红色,或者追溯调整到了root节点,这时删除的修复操作完毕。
RBTree的Java实现
publicclassRBTreeNode
privateTvalue;//nodevalue
privateRBTreeNode
privateRBTreeNode
privateRBTreeNode
privatebooleanred;//colorisredornotred
publicRBTreeNode(){}
publicRBTreeNode(Tvalue){this.value=value;}
publicRBTreeNode(Tvalue,booleanisRed){this.value=value;this.red=isRed;}
publicTgetValue(){
returnvalue;
}
voidsetValue(Tvalue){
this.value=value;
}
RBTreeNode
returnleft;
}
voidsetLeft(RBTreeNode
this.left=left;
}
RBTreeNode
returnright;
}
voidsetRight(RBTreeNode
this.right=right;
}
RBTreeNode
returnparent;
}
voidsetParent(RBTreeNode
this.parent=parent;
}
booleanisRed(){
returnred;
}
booleanisBlack(){
return!
red;
}
/**
*isleafnode
**/
booleanisLeaf(){
returnleft==null&&right==null;
}
voidsetRed(booleanred){
this.red=red;
}
voidmakeRed(){
red=true;
}
voidmakeBlack(){
red=false;
}
@Override
publicStringtoString(){
returnvalue.toString();
}
}
publicclassRBTree
privatefinalRBTreeNode
//nodenumber
privatejava.util.concurrent.atomic.AtomicLongsize=
newjava.util.concurrent.atomic.AtomicLong(0);
//inoverwritemode,allnode'svaluecannothassamevalue
//innon-overwritemode,nodecanhavesamevalue,suggestdon'tusenon-overwritemode.
privatevolatilebooleanoverrideMode=true;
publicRBTree(){
this.root=newRBTreeNode
}
publicRBTree(booleanoverrideMode){
this();
this.overrideMode=overrideMode;
}
publicbooleanisOverrideMode(){
returnoverrideMode;
}
publicvoidsetOverrideMode(booleanoverrideMode){
this.overrideMode=overrideMode;
}
/**
*numberoftreenumber
*@return
*/
publiclonggetSize(){
returnsize.get();
}
/**
*gettherootnode
*@return
*/
privateRBTreeNode
returnroot.getLeft();
}
/**
*addvaluetoanewnode,ifthisvalueexistinthistree,
*ifvalueexist,itwillreturntheexistvalue.otherwisereturnnull
*ifoverridemodeistrue,ifvalueexistinthetree,
*itwilloverridetheoldvalueinthetree
*
*@paramvalue
*@return
*/
publicTaddNode(Tvalue){
RBTreeNode
returnaddNode(t);
}
/**
*findthevaluebygivevalue(includekey,keyusedforsearch,
*otherfieldisnotused,@seecomparemethod).ifthisvaluenotexistreturnnull
*@paramvalue
*@return
*/
publicTfind(Tvalue){
RBTreeNode
while(dataRoot!
=null){
intcmp=dataRoot.getValue().compareTo(value);
if(cmp<0){
dataRoot=dataRoot.getRight();
}elseif(cmp>0){
dataRoot=dataRoot.getLeft();
}else{
returndataRoot.getValue();
}
}
returnnull;
}
/**
*removethenodebygivevalue,ifthisvaluenotexistsintreereturnnull
*@paramvalueincludesearchkey
*@returnthevaluecontainintheremovednode
*/
publicTremove(Tvalue){
RBTreeNode
RBTreeNode
while(dataRoot!
=null){
intcmp=dataRoot.getValue().compareTo(value);
if(cmp<0){
parent=dataRoot;
dataRoot=dataRoot.getRight();
}elseif(cmp>0){
parent=dataRoot;
dataRoot=dataRoot.getLeft();
}else{
if(dataRoot.getRight()!
=null){
RBTreeNode
//xusedforfixcolorbalance
RBTreeNode
min.getParent():
min.getRight();
booleanisParent=min.getRight()==null;
min.setLeft(dataRoot.getLeft());
setParent(dataRoot.getLeft(),min);
if(parent.getLeft()==dataRoot){
parent.setLeft(min);
}else{
parent.setRight(min);
}
setParent(min,parent);
booleancurMinIsBlack=min.isBlack();
//inheritdataRoot'scolor
min.setRed(dataRoot.isRed());
if(min!
=dataRoot.getRight()){
min.setRight(dataRoot.getRight());
setParent(dataRoot.getRight(),min);
}
//removeablacknode,needfixcolor
if(curMinIsBlack){
if(min!
=dataRoot.getRight()){
fixRemove(x,isParent);
}elseif(min.getRight()!
=null){
fixRemove(min.getRight(),false
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 红黑树 深入 剖析 Java 实现
![提示](https://static.bingdoc.com/images/bang_tan.gif)