您的位置:首页 > 编程语言 > C语言/C++

红黑树插入操作的C++实现

2012-11-04 00:01 483 查看
红黑树是具有如下顺序属性的二叉查找树

1、每个节点要么是红色,要么是黑色

2、根是黑色

3、所有叶子节点都是黑色(叶子是NIL节点)

4、每个红色节点的两个孩子节点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色节点)

5、从根节点到NIL指针的每条路径必须包含相同数目的黑色节点

对NIL节点的理解是它不包含数据只充当树在此结束的指示

红黑树的插入的时候,把新插入的节点设置成红色,这样不会造成某一个分子的黑色节点数目超过其它分支的数目。但这样可能会违背性质4的要求,之所以选择违背性质4,是因为如果某一个分支多了一个黑色的节点很难进行调整。但如果只是颜色不符合规定,则相对容易调整。

删除操作太过复杂,未做实现。以后待定

用父代表当前插入节点的父节点,用祖父代表当前插入节点的父节点的父节点

调整的规则如下

1、父为黑,不需做额外的调整,设置好父子关系即可

2、父为红。父为红时,必定有祖父节点,用叔父代表其父节点的兄弟节点。

根据叔父节点颜色的不同,分两种情况讨论

2.1 叔父为红。(将其父和叔父变黑,将其祖父变红,然后递归向上检查。)

2.2 叔父为黑。根据子在父的方向与父在祖父方向的不同,又分两种情况

2.2.1 子在父与父在祖父的左右方向不同。

通过对子和父进行旋转,使得变成情况2.2.2

(子在父右则左旋,子在父左则右旋)

2.2.2 子在父与父在祖父的左右方向相同。

左旋或者右旋,使得父变成祖父和当前插入节点的父节点

然后将父节点变黑,祖父节点变红即可

(都在左,则右旋;都在右,则左旋)

下面是实现代码

/*

红黑树是具有如下顺序属性的二叉查找树

1、每个节点要么是红色,要么是黑色

2、根是黑色

3、所有叶子节点都是黑色(叶子是NIL节点)

4、每个红色节点的两个孩子节点都是黑色(从每个叶子到根的所有路径上不能有两个连续的红色节点)

4、从根节点到NIL指针的每条路径必须包含相同数目的黑色节点

对NIL节点的理解是它不包含数据只充当树在此结束的指示

*/

#include<iostream>

using std::ostream;

enum Color{red,black};

class TreeNode

{

private:

int value;

Color color;

TreeNode* lchild;

TreeNode* rchild;

TreeNode* parent;

/*构造一个专门的叶子节点*/

static TreeNode* NIL;

public:

TreeNode();

Color getColor()const;

TreeNode* getLeftChild()const;

TreeNode* getRightChild()const;

TreeNode* getParent() const;

int getValue() const;

private:

/*辅助函数,用来计算从当前节点开始,直到根节点总共有多少个黑色节点*/

int get_black_num();

friend class Tree;

friend ostream& operator<<(ostream&,const Tree&);

};

class Tree

{

private:

TreeNode* root;

public:

TreeNode* getRoot() const;

Tree();

void insert(int);

/*连续插入*/

void insert(int[] ,int);

void remove(int);

/*检查树的性质是否符合要求*/

bool check_tree();

/*给出要查询的值。找到返回0,否则返回-1*/

//int find(int);

private:

//插入过程中的一些辅助函数

/*对数进行调整*/

void updateTree(TreeNode*);

/*左旋*/

void leftRotate(TreeNode*);

/*右旋*/

void rightRotate(TreeNode*);

friend ostream& operator<<(ostream&,const Tree&);

};

/*打印红黑树用*/

ostream& operator<<(ostream& os,const Tree& tree);

#include"red_black_tree.h"

#include<vector>

using std::vector;

using std::cout;

using std::endl;

/*

TreeNode类的相关定义

*/

TreeNode* TreeNode::NIL=NULL;

TreeNode::TreeNode()

{

lchild=rchild=parent=NULL;

color=black;

}

int TreeNode::get_black_num()

{

int num=0;

TreeNode* p=this;

while(p)

{

if(p->color==black)

num++;

p=p->parent;

}

return num;

}

Color TreeNode::getColor() const

{

return color;

}

TreeNode* TreeNode::getLeftChild() const

{

return lchild;

}

TreeNode* TreeNode::getRightChild() const

{

return rchild;

}

TreeNode* TreeNode::getParent() const

{

return parent;

}

int TreeNode::getValue() const

{

return value;

}

/*

Tree类的函数定义

*/

Tree::Tree()

{

root=NULL;

if(!TreeNode::NIL)

{

TreeNode::NIL=new TreeNode;

TreeNode::NIL->color=black;

TreeNode::NIL->parent=NULL;

TreeNode::NIL->lchild=NULL;

TreeNode::NIL->rchild=NULL;

}

}

TreeNode* Tree::getRoot() const

{

return root;

}

void Tree::insert(int array[] ,int num)

{

for(int i=0;i<num;i++)

{

/*

cout<<"before insert "<<array[i]<<endl;

cout<<*this<<endl;

*/

insert(array[i]);

/*

cout<<"after insert "<<array[i]<<endl;

cout<<"check tree "<<this->check_tree()<<endl;

if(!this->check_tree())

break;

cout<<*this<<endl;

cout<<"..........................."<<endl;

*/

}

}

void Tree::updateTree(TreeNode* node)

{

if(node==root)

node->color=black;

else

{

//父为黑,返回即可。父子关系的设置在insert里面已经完成。本身节点为黑也可以直接返回即可

if(node->parent->color==black||node->color==black)

return;

else //父为红,必有祖父节点

{

TreeNode* grand=node->parent->parent;

TreeNode* parent=node->parent;

TreeNode* uncle=grand->lchild==parent?grand->rchild:grand->lchild;

if(uncle->color==red) //叔父节点也为红

{

uncle->color=black;

parent->color=black;

grand->color=red;

updateTree(grand);

}

else //父为红,叔父为黑

{

enum Dir{left,right};

Dir pDir=(parent==grand->lchild)?left:right;

Dir dir=(node==parent->lchild)?left:right;

if(pDir==dir) //子父同向

{

if(dir==left)

rightRotate(parent);

else

leftRotate(parent);

parent->color=black;

grand->color=red;

}

else //子父不同向

{

if(dir==left)

rightRotate(node);

else

leftRotate(node);

/*左旋或者右旋之后,从其子节点开始调整*/

updateTree(node->lchild);

updateTree(node->rchild);

}

}

}

}

}

void Tree::insert(int data)

{

if(!root) //如果树为空,就新建根节点

{

root=new TreeNode;

root->parent=NULL;

root->lchild=TreeNode::NIL;

root->rchild=TreeNode::NIL;

root->value=data;

root->color=black;

}

else

{

TreeNode* p=root; //用p来指向待插入节点的父节点

TreeNode* temp=data>(p->value)?p->rchild:p->lchild;

while(temp!=TreeNode::NIL)

{

p=temp;

temp=data>(p->value)?p->rchild:p->lchild;

}

/*新建一个红节点作为p的子节点插入,然后对树进行调整*/

TreeNode* newNode=new TreeNode;

newNode->color=red;

newNode->parent=p;

newNode->value=data;

newNode->lchild=newNode->rchild=TreeNode::NIL;

if(data<p->value)

p->lchild=newNode;

else

p->rchild=newNode;

updateTree(newNode);

}

}

/*

左旋

左旋之后的效果便是使p的父节点成为p左孩子节点。

p的左孩子节点成为p的父节点的孩子节点

*/

void Tree::leftRotate(TreeNode* node)

{

TreeNode* cur_left=node->lchild;

TreeNode* parent=node->parent;

TreeNode* grand=parent->parent;

/*代表其父节点在祖父节点的方向 1代表左,2代表右*/

if(grand)

{

int i=grand->lchild==parent?1:2;

/*对grand来说,改变的是左孩子或右孩子*/

if(i==1)

grand->lchild=node;

else

grand->rchild=node;

}

else

root=node;

/*对noe来说改变的是它的左孩子,和父节点*/

node->lchild=parent;

node->parent=grand;

/*对parent来说,改变的是父节点,右孩子*/

parent->parent=node;

parent->rchild=cur_left;

/*对cur_left来说,改变的是它的父节点*/

cur_left->parent=parent;

}

void Tree::rightRotate(TreeNode* node)

{

TreeNode* cur_right=node->rchild;

TreeNode* parent=node->parent;

TreeNode* grand=parent->parent;

/*若grand为NULL,则说明parent是root*/

if(grand)

{

/*代表其父节点在祖父节点的方向 1代表左,2代表右*/

int i=0;

i=grand->lchild==parent?1:2;

/*grand改变左子树或者右子树*/

if(i==1)

grand->lchild=node;

else

grand->rchild=node;

}

else

root=node;

/*node节点改变右子树和父节点*/

node->rchild=parent;

node->parent=grand;

/*parent节点改变父节点和左子树*/

parent->parent=node;

parent->lchild=cur_right;

/*cur_right改变父节点*/

cur_right->parent=parent;

}

/**/

void Tree::remove(int data)

{

}

/*

将一棵红黑树按照层次结构输出

*/

ostream& operator<<(ostream& os,const Tree& tree)

{

TreeNode* root=tree.getRoot();

if(!root)

return os;

vector<TreeNode*> outQueue;

outQueue.push_back(root);

int level=0;

while(outQueue.size()>0)

{

vector<TreeNode*> tempQueue=outQueue;

outQueue.clear();

os<<"[ level= "<<level<<" ] { ";

for(int i=0;i<tempQueue.size();i++)

{

TreeNode* node=tempQueue[i];

TreeNode* parent=node->parent;

Color color=tempQueue[i]->getColor();

int value=node->getValue();

cout<<" [ "<<value;

if(node->getLeftChild()!=TreeNode::NIL)

outQueue.push_back(node->getLeftChild());

if(node->getRightChild()!=TreeNode::NIL)

outQueue.push_back(node->getRightChild());

if(color==red)

cout<<" red ";

else

cout<<" black ";

if(parent)

{

cout<<" parent ="<<parent->value;

if(node==parent->lchild)

cout<<" left ";

else

cout<<" right ";

}

else

cout<<" root";

cout<<" ] ";

}

os<<" }"<<endl;

level++;

tempQueue.clear();

}

return os;

}

/*

检查树是否符合红黑树的五条规定

同时还检查父子节点之间的数值大小是否符合二叉查找树的规定

父子节点之间的指针是否指向正确

*/

bool Tree::check_tree()

{

vector<TreeNode*> nodes;

/*

保存所有的指向叶子节点的节点,用于计算比较从此节点往上是否符合规则5

由于NIL是一个共享的节点,所以这里用指向NIL的节点来验证规则5

*/

vector<TreeNode*> leafs;

bool result=true;

if(!root)

return result;

nodes.push_back(root);

int i=0;

while(result&&i<nodes.size())

{

TreeNode* p=nodes[i];

if(p->lchild==TreeNode::NIL) //如果左子树为叶子

{

leafs.push_back(p);

/*如果右子树不为叶子。右子树也为叶子的情况在上一条语句中已经包含*/

if(p->rchild!=TreeNode::NIL)

{

nodes.push_back(p->rchild);

result=result&&(p==p->rchild->parent);

result=result&&(p->value<=p->rchild->value);

}

}

else //如果左子树不为叶子

{

nodes.push_back(p->lchild);

result=result&&(p==p->lchild->parent);

result=result&&(p->value>=p->lchild->value);

if(p->rchild==TreeNode::NIL) //如果右子树为叶子

leafs.push_back(p);

else //左右子树都不是叶子

{

result=result&&(p==p->rchild->parent);

result=result&&(p->value<=p->rchild->value);

nodes.push_back(p->rchild);

}

}

if(p->color==red)

result=result&&(p->lchild->color==black)&&(p->rchild->color==black);

i++;

}

int num=-1;

for(i=0;result&&(i<leafs.size());i++)

{

if(num==-1)

num=leafs[i]->get_black_num();

else

result=result&&(num==leafs[i]->get_black_num());

}

return result;

}

int main()

{

Tree tree;

int testData[]={15,13,2,34,53,12,21,23,52,75,46,72,19,24,87,98,1,3,31,28,123,214,512,5,78,98};

tree.insert(testData,sizeof(testData)/sizeof(int));

cout<<tree<<endl;

}

上述代码运行的结果如下图所示



如果把插入过程的数据打印显示出来则如下(数据量比较大,只选取了部分做截图)





内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: