红黑树的节点插入算法实现

来源:互联网 发布:唯品会有自动抢购软件 编辑:程序博客网 时间:2024/04/30 01:00

参照算法导论简单实现了一下红黑树的节点插入,对于多次static_cast的使用,我表示面向对象那块学的不好,谁有好的方法可以告诉我一下。


首先实现一棵二叉查找树,类的声明如下:

typedef int treeKeyType;class BinSearchTreeNode{public:BinSearchTreeNode(treeKeyType k):key(k){left=NULL;right=NULL;parent=NULL;}BinSearchTreeNode * left;BinSearchTreeNode * right;BinSearchTreeNode * parent;treeKeyType key;};class BinSearchTree{public:BinSearchTreeNode * root;BinSearchTree():root(NULL){}~BinSearchTree(){}// 中序遍历void inOrderPrint(BinSearchTreeNode *);// 前序遍历void preOrderPrint(BinSearchTreeNode *);// 后序遍历void proOrderPrint(BinSearchTreeNode *);// 中序遍历void inOrder();// 前序遍历void preOrder();// 后序遍历void proOrder();// find the node who is equal to keyBinSearchTreeNode * treeSearch(treeKeyType key);// return the minimum value of tree which has root xBinSearchTreeNode * minNode(BinSearchTreeNode * x);// return the maximum value of tree which has root xBinSearchTreeNode * maxNode(BinSearchTreeNode * x);// return the successor of node xBinSearchTreeNode * successor(BinSearchTreeNode * x);// return the predessor of node xBinSearchTreeNode * predessor(BinSearchTreeNode * x);// insert node z into the treevoid insertNode(BinSearchTreeNode * newNode);// delete node z from the treevoid deleteNode(BinSearchTreeNode * z);};


因为红黑树也有孩子节点,父亲节点,关键词等成员,这点与二叉查找树的节点非常类似,所以我们红黑树的节点直接继承自二叉查找树的节点,只不过多了一个颜色成员color。

但是红黑树相比二叉查找树来说,必须满足以下五条性质:

(1)每个节点或者红,或者黑;

(2)根节点必须是黑的;

(3)每个叶子节点(或者说NIL,空节点)是黑的;

(4)每一个红的节点的父亲节点必须是黑的;

(5)对于每个节点,从该节点到其子孙节点的所有路径上包含相同数目的黑节点,我们将这个数目成为黑高度。

首先,介绍一下红黑树的左旋(left-rotate)和右旋(right-rotate)操作,这两个操作是互逆的。

#define RED 0#define BLACK 1typedef int Color;class RedBlackTreeNode : public BinSearchTreeNode{public:Color color;RedBlackTreeNode(treeKeyType key, Color c_color=BLACK):BinSearchTreeNode(key), color(c_color){}};

首先,介绍一下红黑树的左旋(left-rotate)和右旋(right-rotate)操作,这两个操作是互逆的。

左旋操作的过程如下:

(1)首先将x设置为y的左孩子;

(2)y的左孩子Beta的父亲设置为为x;

(3)将y的父亲节点设置为x的父亲节点;

(4)判断一下x以前是否作为根节点,如果是,将当前树的跟改成y;接下来再判断x以前作为左孩子还是右孩子,如果是左孩子,将y设置为x的爷爷的左孩子,否则设置为x的爷爷的右孩子;

(5)x成为y的左孩子,y成为x的父亲。

右旋,反之。。。



左旋和右旋的实现代码如下:

// 左旋操作void RedBlackTree::leftRotate(RedBlackTreeNode * x){RedBlackTreeNode * y=static_cast<RedBlackTreeNode *>(x->right);x->right=y->left;if(y->left != NULL)y->left->parent=x;y->parent=x->parent;if(x->parent == NULL)//then x is current rootroot=y;else if(x->parent->right == x)x->parent->right=y;elsex->parent->left=y;y->left=x;x->parent=y;}// 右旋操作void RedBlackTree::rightRotate(RedBlackTreeNode * y){RedBlackTreeNode * x=static_cast<RedBlackTreeNode *>(y->left);y->left=x->right;if(x->right != NULL)x->right->parent=y;if(y->parent == NULL)//then y is currrent root root=x;else if(y == y->parent->left)y->parent->left=x;elsey->parent->right=x;x->right=y;y->parent=x;}

红黑树的插入,实现如下:

在调用修复函数之前的插入操作,都和二叉查找树的插入操作类似,用两个指针x和y分别代表父子节点,一级级的往下找,直到x为空,y作为插入位置(即插入节点,作为y的孩子),判断y的关键词的大小和要插入节点关键词的大小来确定是作为y的左孩子还是右孩子。同时记住将新插入的节点z的颜色置为红色,z的两个孩子节点作为黑色的NIL节点(就是NULL),这样保证不改变树的黑高度。

// 插入节点void RedBlackTree::insertNode(RedBlackTreeNode * z){// first, do the insertion like Binary Search TreeRedBlackTreeNode * x=static_cast<RedBlackTreeNode *>(root), * y=NULL;while(x != NULL){y=x;if(z->key < x->key)x=static_cast<RedBlackTreeNode *>(y->left);elsex=static_cast<RedBlackTreeNode *>(y->right);}z->parent=y;if(y == NULL)//说明当前树为空root=z;else if(z->key < y->key)y->left=z;elsey->right=z;z->left=NULL;z->right=NULL;z->color=RED;//调用红黑树修复函数insertFixUp(z);}

红黑树的修复操作是为了保持红黑树的性质,实现如下:

这里分三种情况:case1,case2和case3,如图所示。


case 1

图中z是当前要插入的节点,只有当z的父亲为红色时,我们才有必要执行修复操作;因为如果z的父亲为黑色的话,红黑树性质(4):所有红色节点的父亲必须为黑色也会满足,从而所有红黑树的性质都满足了,直接退出算法就可以。无论何种情况,新插入的节点都要置为红色以保证黑高度不变。

case1代表的情况是z的叔叔(叔叔就是爷爷的相对于爸爸的另一个孩子)也是红色的。这时将z的父亲、叔叔和爷爷的颜色取反。让z指向新的位置,即z的爷爷。继续循环。


case 2 and case 3

如果z的叔叔是黑色的,那就进入case2或者case3的情况,可能是先执行case2后执行case3,也可能只执行case3,无论如何,case3都是要执行的。

case2代表的情况是当前要插入的节点z是其父亲A的右孩子,更一般的情况,应该说z和其父亲之间的连线l1和z的父亲和z的爷爷之间的连接线l2成一个锐角。(估计大家应该能明白是什么意思,就是B是A的右孩子,A是C的左孩子。但是我们在考虑case1,case2,case3时,仅仅考虑了一半的情况:即z的父亲A是z的爷爷z的左孩子,另一半情况当然是A是C的右孩子了!那些情况也会分case1,case2,case3,跟现在讨论的完全类似。)对于case2,对z的父亲做一次左旋操作,就会变成case3的情况。然后在z的爷爷位置做一次右旋操作,同时将z的父亲和z的爷爷的颜色取反。此时,z的父亲变为黑色了,(我们认为z的父节点如果变为黑色后就满足红黑树性质了),循环自动退出。

总的来说,case1有可能执行多次,但是case2和case3只有可能执行一次。

// 需要进行修复,保持红黑树的黑高度一致等性质不变void RedBlackTree::insertFixUp(RedBlackTreeNode * z){while( z->parent != NULL && static_cast<RedBlackTreeNode *>(z->parent)->color == RED ){if(z->parent->parent->left == z->parent){RedBlackTreeNode * ancle =static_cast<RedBlackTreeNode *>(z->parent->parent->right);if(ancle->color == RED)// case1{static_cast<RedBlackTreeNode *>(z->parent->parent)->color=RED;static_cast<RedBlackTreeNode *>(z->parent)->color=BLACK;ancle->color=BLACK;z=static_cast<RedBlackTreeNode *>(z->parent->parent);}else {if(z->parent->right == z)// case2{z=static_cast<RedBlackTreeNode *>(z->parent);leftRotate(z);}// case3static_cast<RedBlackTreeNode *>(z->parent)->color=BLACK;static_cast<RedBlackTreeNode *>(z->parent->parent)->color=RED;rightRotate(static_cast<RedBlackTreeNode *>(z->parent->parent));}}else // 当前节点的父亲是当前节点爷爷的右孩子{RedBlackTreeNode * ancle=static_cast<RedBlackTreeNode *>(z->parent->parent->left);if(ancle->color == RED)// case1{static_cast<RedBlackTreeNode *>(z->parent->parent)->color=RED;static_cast<RedBlackTreeNode *>(z->parent)->color=BLACK;ancle->color=BLACK;z=static_cast<RedBlackTreeNode *>(z->parent->parent);}else{if( z == z->parent->left )// case2{z=static_cast<RedBlackTreeNode *>(z->parent);rightRotate(z);}// case3static_cast<RedBlackTreeNode *>(z->parent->parent)->color=RED;static_cast<RedBlackTreeNode *>(z->parent)->color=BLACK;leftRotate(static_cast<RedBlackTreeNode *>(z->parent->parent));}}}}




原创粉丝点击