由于只看到13.3节,所以暂时只实现旋转和插入函数。
思考:如果不带父结点,那么在需要访问z的父结点时,我们可以借助查找函数从根结点到待查找结点z的路径上必然能找到z的父结点和祖父结点,所以由此可访问父结点。不过这种方法使得旋转函数时间增加到O(lgn),而插入函数也相应的增加到O(lgnlgn)。节省了空间上的内存,同时增加了运行时间。
具体代码如下:
#include <iostream> using namespace std; #define BLACK 0 #define RED 1 #define Nil -1 #define LEN sizeof(struct Tree) struct Tree { struct Tree*left; struct Tree*right; int key; int color; }; struct Tree*root=NULL; struct Tree*nil=NULL; //非递归版本的二叉查找树查找函数 struct Tree*ITERATIVE_TREE_SEARCH(struct Tree*x,int k,struct Tree*&p1,struct Tree*&p2) {// while (x!=nil&&k!=x->key) { p1=x; if (k<x->key) { x=x->left; } else x=x->right; if(k!=x->key)//如果没找到了待查找值,那么继续记录其祖父和父结点值。 { p2=p1; p1=x; } } return x; } void LEFT_ROTATE(struct Tree*T,struct Tree*x) {//左旋转:分三个步骤①②③来叙述旋转代码的。 struct Tree*p1=nil,*p2=nil; struct Tree*y=x->right;//设置y结点。 x->right=y->left;//本行代码以及下面的if结构表达的是“y的左孩子成为x的右孩子”。① ITERATIVE_TREE_SEARCH(root,x->key,p1,p2); if(p1==nil)//本行代码以及下面的if-else结构表达的过程是“y成为该子树新的根”。② { root=y; } else if(x==p1->left) { p1->left=y; } else p1->right=y; y->left=x;//本行代码以及下面一行都表达了“x成为y的左孩子”。③ } void RIGHT_ROTATE(struct Tree*T,struct Tree*x) {//右旋转 struct Tree*p1=nil,*p2=nil; struct Tree*y=x->left; x->left=y->right; ITERATIVE_TREE_SEARCH(root,x->key,p1,p2); if(p1==nil) { root=y; } else if(x==p1->right) { p1->right=y; } else p1->left=y; y->right=x; } void RB_INSERT_INSERT_FIXUP(struct Tree*T,struct Tree*z) { struct Tree*p1=nil,*p2=nil; ITERATIVE_TREE_SEARCH(root,z->key,p1,p2);//p1=z->parent,p2=z->parent->parent while (1) { ITERATIVE_TREE_SEARCH(root,z->key,p1,p2);//p1是父结点 p2是祖父结点 if (p1->color!=RED) { break; } if (p1==p2->left) { struct Tree*y=p2->right;//叔结点 if (y->color==RED)//情况一:叔结点为红色 {//给p1,y,p2着色以保持性质5。并且解决了z的父结点和z都是红色结点问题 p1->color=BLACK; y->color=BLACK; p2->color=RED; z=p2;//把z的祖父结点当成新结点z进入下一次循环 } else { if (z==p1->right)//情况二:检查z是否是一个右孩子且叔结点为黑色,前提是p1结点不是叶子结点 {//使用一个左旋让情况2转变为情况3 z=p1; LEFT_ROTATE(T,z);//由于进入if语句后可知旋转结点不可能是叶子结点,这样就不用判断z是否是叶子结点了。 ITERATIVE_TREE_SEARCH(root,z->key,p1,p2);//p1=z->parent,p2=z->parent->parent } p1->color=BLACK;//情况三:是z是一个左孩子且叔结点为黑色,改变z的父和祖父结点颜色并做一次右旋,以保持性质5。 p2->color=RED; if(p2!=nil) RIGHT_ROTATE(T,p2);//由于p2可能是叶子结点,所以最好还是用一个if判断 } } else//下面else分支类似于上面 { struct Tree*y=p2->left; if (y->color==RED) { p1->color=BLACK; y->color=BLACK; p2->color=RED; z=p2; } else { if (z==p1->left) { z=p1; RIGHT_ROTATE(T,z); ITERATIVE_TREE_SEARCH(root,z->key,p1,p2); } p1->color=BLACK; p2->color=RED; if(p2!=nil) LEFT_ROTATE(T,p2); } } } root->color=BLACK;//最后给根结点着为黑色。 } void RB_INSERT(struct Tree*T,struct Tree*z) { struct Tree*y=nil; struct Tree*x=root; while (x!=nil) { y=x; if (z->key<x->key) { x=x->left; } else x=x->right; } if (y==nil) { root=z; } else if(z->key<y->key) { y->left=z; } else y->right=z; z->left=nil; z->right=nil; z->color=RED; RB_INSERT_INSERT_FIXUP(T,z); } //中序遍历 void InOderTraverse(struct Tree *p) { if (p!=nil) { InOderTraverse(p->left); cout<<p->key<<" "<<p->color<<" "<<endl; InOderTraverse(p->right); } } void main() { nil=new struct Tree[LEN]; nil->key=Nil;nil->color=BLACK; root=nil; int i=0; struct Tree*ROOT=new struct Tree[LEN]; cin>>ROOT->key; RB_INSERT(nil,ROOT); root=ROOT; while (i!=12) { struct Tree*z=new struct Tree[LEN]; cin>>z->key; RB_INSERT(root,z); i++; } InOderTraverse(root); }
不带父结点的红黑树实现
时间: 2024-10-10 09:49:25