为了进一步了解RB树,先了解一下二叉查找树
定义:
二叉查找树是一棵二叉树,因此可以用链式结构来存储数据。若二叉查找树不为空,则应具有以下性质:
- 关键字的值唯一
- 若左子树不为空,则子树任何节点关键字值一定小于其根节点的关键字值
- 若右子树不为空,则子树任何节点关键字值一定大于其根节点的关键字值
- 左、右子树任然是二叉查找树
结构示意图
查找节点:
- 若二叉查找树为空,则查找失败
- 若该树非空且查找数据x等于根节点的值,则查找成功,返回根节点
- 若该树非空且查找数据x小于根节点的值,则查找左子树,直到值相等,并返回节点
- 若该树非空且查找数据x大于根节点的值,则查找右子树,直到值相等,并返回节点
插入节点:
- 若二叉查找树为空,则将结点作为根节点插入
- 若所插入节点关键字值等于根节点的值,则插入失败,并返回
- 若所插入节点关键字值小于根节点的值,则把节点插入到左子树中
- 若所插入节点关键字值大于根节点的值,则把节点插入到右子树中
删除节点:
- 若p结点为叶子结点,则删去该叶子结点,修改其双亲结点的指针即可。
- 若p结点只有左子树PL或右子树PR,此时只要令PL或PR直接成为其双亲结点的左子树(当p是左子树)或右子树(当p是右子树)。
- 若p结点的左子树和右子树均不空。找出节点p的后继节点y(一定在节点p的右子树中),以右子树中的最小数作为后继节点。
下面是实现这些功能的程序
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#define LENGTH 11
struct BinSearNode
{
int key;
struct BinSearNode *left_child;
struct BinSearNode *right_child;
struct BinSearNode *parent;
}Node;
typedef struct BinSearNode *PNode;
/*Search the node of the tree*/
PNode Search_Tree(PNode root,int key)
{
PNode x=root;
//the tree is not empty and the key is not equal
while(NULL!=x && x->key!=key)
{
if(x->key<key)
x=x->right_child;//along the right child of tree,until it is empty
else
x=x->left_child;//along the left child of tree,until it is empty
}
return x;//return the node
}
/*the minimum key of node in the tree*/
PNode Minimum_Tree(PNode root)
{
PNode x=root;
while(NULL!=x->left_child)
{
x=x->left_child;
}
return x;
}
/*the maxmum key of node in the tree*/
PNode Maxmum_Tree(PNode root)
{
PNode x=root;
while(NULL!=x->right_child)
{
x=x->right_child;
}
return x;
}
/*the successor node of the x,后继节点可以这么理解,将查找树从小到大排序,比他大的值
如果有右孩子,那么应该是右子树当中的最小值
如果没有右孩子,那么应该向上追溯,直至一个分支节点是其父节点的左孩子,返回父节点
可以用于operate++*/
PNode Successor_Tree(PNode x)
{
PNode y=NULL;
//case 1:the right subtree of node x is not empty
if(NULL!=x->right_child)
{
y=Minimum_Tree(x->right_child);
}
//case 2:the right subtree of node x is empty
//and the node of x has a successor node y
else
{
y=x->parent;
while(NULL!=y && x==y->right_child)
{
x=y;
y=y->parent;
}
}
return y;
}
/*the predecessor node of the x,前任节点
如果节点有左孩子,那么找到左孩子的最大值
如果没有左孩子,那么向上追溯直至一个分支当前节点是对应父节点的右孩子,返回父节点的值
*/
PNode Predecessor_Tree(PNode x)
{
PNode y=NULL;
//case 1:the left subtree of node x is not empty
if(NULL!=x->left_child)
{
y=Maxmum_Tree(x->left_child);
}
//case 2:the left subtree of node x is empty
//and the node of x has a predecessor node y
else
{
y=x->parent;
while(NULL!=y && x==y->left_child)
{
x=y;
y=y->parent;
}
}
return y;
}
/*insert a new node into the BST*/
void Insert_Tree(PNode *root,int key)
{
PNode x=*root;
PNode y=NULL;
PNode z=(PNode)malloc(sizeof(Node));//<开辟一个节点的空间
if(NULL==z)
{
printf("malloc the z is failed.");
exit(1);
}
//initial the node of z
z->key=key;
z->left_child=z->right_child=z->parent=NULL;
//Find the location node of y to insert the node of z
while(NULL!=x)
{
y=x; //<找到当前树满足条件的叶子节点了,表示为y,之后在y节点后面进行插入操作
if(z->key<x->key)
x=x->left_child;
else
x=x->right_child;
}
//insert the node of z
z->parent=y;
if(NULL==y)
*root=z;//tree was empty
else
{
if(z->key<y->key)
y->left_child=z;
else
y->right_child=z;
}
}
void Transplant(PNode *root,PNode u,PNode v)
{
if(NULL==u->parent)
*root=v;
else
{
if(u==u->parent->left_child)
u->parent->left_child=v;
else
u->parent->right_child=v;
}
if(NULL!=v)
v->parent=u->parent;
}
/*delete a node in the binary search tree*/
void Delete_Tree(PNode *root,int key) //<删除节点
{
//Find the node you want to delete
PNode p=Search_Tree(*root,key);
if(NULL==p->left_child)
Transplant(root,p,p->right_child);
else
{
if(NULL==p->right_child)
Transplant(root,p,p->left_child);
else
{
PNode y=Successor_Tree(*root);
if(y->parent!=p)
{
Transplant(root,y,y->right_child);
y->right_child=p->right_child;
y->right_child->parent=y;
}
Transplant(root,p,y);
y->left_child=p->left_child;
y->left_child->parent=y;
}
}
}
/*print the key of binary search tree*/
void ioder_Tree(PNode root)
{
if(NULL!=root)
{
ioder_Tree(root->left_child);
printf(" %d",root->key);
ioder_Tree(root->right_child);
}
}
int main()
{
int i;
int Arr[LENGTH]={16,6,20,2,7,19,22,1,4,11,8};
PNode root=NULL;
PNode p=NULL;
for(i=0;i<LENGTH;i++)
{
Insert_Tree(&root,Arr[i]);
}
ioder_Tree(root);
printf("\n");
//printf("Hello world!\n");
Delete_Tree(&root,11);
ioder_Tree(root);
printf("\n");
p=Maxmum_Tree(root);
printf("The Maxmum of node is:%d\n",p->key);
p=Minimum_Tree(root);
printf("The Minimum of node is:%d\n",p->key);
return 0;
}
结果图:
在STL中除了要求是搜索二叉树之外,还需要是平衡搜索二叉树:
因为关联容器很重要的一个是搜索效率
平衡树有很多种比如AVL_tree,RB-tree,AA-tree
平衡二叉树插入节点和删除节点的平均时间都是比较长,但是他们可以很好的避免难以应付的最坏情况
AVL-tree要求任何节点的左右子树的高度相差最多为1
如果插入或者删除操作打破了这些平衡条件,那么这样的树应该需要做相应的旋转操作来达到平衡
RB-tree的节点有红色和黑色的区分,并有一些限制条件,之后会专门进行介绍