二叉搜索树
每个节点除关键字外,还包含属性left,right,p,分别指向其左孩子,右孩子,和双亲
且对任何节点x,其左子树的最大关键字不超过x.key,其右子树中的关键字最小不低于x.key。
本程序中的结点定义:
typedef struct Node
{
struct Node * p;
struct Node * left;
struct Node * right;
int key;
}Node;
中序遍历
中序递归遍历二叉搜索树,能够按序(小到大)输出二叉树中的所有关键字,根的关键字位于左右孩子关键字之间。
中序遍历耗费的是一个线性的时间。
void Inorder_Tree_Walk(Node * T) //中序遍历树T,输出
{
if (T != NULL)
{
Inorder_Tree_Walk(T->left); //递归其左孩子
printf("%d\n",T->key); //输出根的关键字
Inorder_Tree_Walk(T->right); //递归其右孩子
}
}
查询
查询的几个操作都能在O(h)内完成,h为二叉树的高度。
查找指定元素
在二叉树中查找指定元素,若存在,返回其结点地址,若不存在,返回空;
T为树根
Node * Tree_Search(Node *T ,int k) //寻找数k是否在T树中,且返回数k的结点地址
{
while(T !=NULL && T->key != k) //按大小遍历树
{
if ( k < T->key) //若小于其T的关键字,则遍历其左孩子
T=T->left;
else
T=T->right;
}
if ( T == NULL) //没找到数k,返回空
{
return NULL;
}
else
{
return T;
}
}
查找最大/小关键字元素
查找二叉树中最大或最小的元素,按照二叉树的定义:
最大元素一定是沿着right孩子指针不断向下查找,直到遇到第一个NULL;
同理最小元素一定是沿着left孩子指针不断向下查找,直到遇到第一个NULL;
Node * Tree_Minimum(Node * T) //给定树根T,查找树的最小元素结点,并返回其地址
{
while(T->left != NULL)
T=T->left;
return T;
}
int Tree_Maximum(Node * T) //给定树根T,查找树的最大元素结点,并返回其值
{
while(T->right != NULL)
T=T->right;
return T->key;
}
查找指定元素的后继和前驱
1.后继:即查找指点元素按序的下一个结点位置,即下一个比它大的数。
按照搜索二叉树的定义,若其存在右孩子,则它的下一个结点一定在其右孩子的子树中最小的一个,即查找Tree_Minimum(x->right);若不存在右孩子,则位于它的一个祖先结点上,且这个祖先一定是最底层有左孩子的祖先,且这个左孩子也是它的一个祖先...(好晦涩)
。可以这样简单理解,要查x的后继,既然它不存在右孩子,那么说明它是这个子树的最大数,那么它的后继一定要往祖先上找,那就去往上层层找祖先,这个祖先必须满足有左孩子,且这个x必须也要位于祖先的左子树中。这样x就为祖先的左子树中最大的一个了。
int Tree_Successor(Node * T,int t) //找数t的后继,T为树根
{
Node * x=Tree_Search(T,t); //找到元素t的位置
Node * y=NULL;
if ( x->right != NULL)
{
y=Tree_Minimum(x->right);
return y->key;
}
y = x->p;
while( y!=NULL && x==y->right)
{
x=y;
y=y->p;
}
return y->key;
}
2.前驱:同上去理解。
int Tree_Predecessor(Node * T,int t) //找数t的前驱,T为树根
{
Node * x=Tree_Search(T,t); //找到元素t的位置
Node * y=NULL;
if ( x->left!= NULL)
{
return Tree_Maximum(x->left);
}
y = x->p;
while( y!=NULL && x==y->left)
{
x=y;
y=y->p;
}
return y->key;
}
插入结点
即向一个二叉搜索树中插入一个x结点,只需要不断比较x->key与当前结点z->key的大小,若小于,则肯定向z的左子树插入,否则向z的右子树插入,循环比较,直到遇到当前结点的左/右子树为空为止。
Node *Tree_Insert(Node *Root,Node * z) //二叉搜索树插入,返回树的根
{
Node * y=NULL;
Node * x=Root;
while( x != NULL)
{
y=x;
if (z->key < x->key)
x = x->left;
else
x = x->right;
}
z->p = y;
if ( y == NULL) //建立第一个根节点
{
Root = z;
}
else if (z->key < y->key)
{
y->left = z;
}
else
{
y->right = z;
}
return Root;
}
删除结点
删除的情况稍复杂,因为要保证二叉搜索树的性质:
没有孩子节点
若要删除的结点z没有孩子节点,则可以直接删除z结点,只要令其z->p->left\right = NULL即可;
只有一个孩子节点
若结点z只有一个孩子,无论是其左孩子还是右孩子,直接替代z结点位置即可;
举个例子:假如z只有一个右孩子,且z是其双亲的左孩子:
z->p->left = z->right;
z->right->p = z->p ;
z->right->p = z->p ;
有两个孩子节点
找出要删除结点z的后继结点y,这个结点y一定位于结点z的右子树中且是z的右子树中最小的一个元素,所以结点y一定没有左孩子。
分两种情况:
1.结点y是结点z的右孩子:这时用y替换z的位置,结点y之前的右子树还是它的右子树,结点z之前的左子树作为y的左子树。
2.结点y不是结点x的右孩子:先用y的右子树x替换y位置,接着用y替换z的位置。
例程
#include <STDIO.H>
#include <STDLIB.H>
typedef struct Node
{
struct Node * p;
struct Node * left;
struct Node * right;
int key;
}Node;
Node *Tree_Insert(Node *Root,Node * z) //二叉搜索树插入,返回树的根
{
Node * y=NULL;
Node * x=Root;
while( x != NULL)
{
y=x;
if (z->key < x->key)
x = x->left;
else
x = x->right;
}
z->p = y;
if ( y == NULL) //建立第一个根节点
{
Root = z;
}
else if (z->key < y->key)
{
y->left = z;
}
else
{
y->right = z;
}
return Root;
}
Node * Establish_Tree(int *A,int Length) //建立一个节点数为Length的二叉搜索树
{
int i;
Node * node,*Root=NULL;
for (i=0;i<Length;i++)
{
node = (Node *)malloc(sizeof(Node));
node->key=A[i];
node->p=NULL;
node->left=NULL;
node->right=NULL;
Root=Tree_Insert(Root,node); //调用插入函数建立树
}
return Root;
}
void Inorder_Tree_Walk(Node * T) //中序遍历树T,输出
{
if (T != NULL)
{
Inorder_Tree_Walk(T->left);
printf("%d\n",T->key);
Inorder_Tree_Walk(T->right);
}
}
Node * Tree_Minimum(Node * T)
{
while(T->left != NULL)
T=T->left;
return T;
}
int Tree_Maximum(Node * T)
{
while(T->right != NULL)
T=T->right;
return T->key;
}
Node * Tree_Search(Node *T ,int k) //寻找数k是否在树中,且返回数k的地址
{
while(T !=NULL && T->key != k)
{
if ( k < T->key)
T=T->left;
else
T=T->right;
}
if ( T == NULL)
{
return NULL;
}
else
{
return T;
}
}
int Tree_Successor(Node * T,int t) //找数t的后继,T为树根
{
Node * x=Tree_Search(T,t);
Node * y=NULL;
if ( x->right != NULL)
{
y=Tree_Minimum(x->right);
return y->key;
}
y = x->p;
while( y!=NULL && x==y->right)
{
x=y;
y=y->p;
}
return y->key;
}
int Tree_Predecessor(Node * T,int t) //找数t的前驱,T为树根
{
Node * x=Tree_Search(T,t);
Node * y=NULL;
if ( x->left!= NULL)
{
return Tree_Maximum(x->left);
}
y = x->p;
while( y!=NULL && x==y->left)
{
x=y;
y=y->p;
}
return y->key;
}
void Transplant(Node **T,Node *u,Node *v) //用节点v替换节点u
{
if (u->p == NULL) //若u为根节点,赋值给T
*T = v;
else if (u == u->p->left)
u->p->left = v;
else
u->p->right = v;
if ( v!= NULL)
v->p = u->p;
}
Node* Tree_Delete(Node *T,int x) //删除元素 x
{
Node * z=Tree_Search(T,x);
Node * y=NULL;
if (z->left == NULL)
Transplant(&T,z,z->right);
else if (z->right == NULL)
Transplant(&T,z,z->left);
else
{
y = Tree_Minimum(z->right);
if ( y->p != z)
{
Transplant(&T,y,y->right);
y->right = z->right;
y->right->p = y;
}
Transplant(&T,z,y);
y->left = z->left;
y->left->p = y;
}
return T;
}
int main(void)
{
int A[]={3,10,2,5,9,7,4,1,6,8};
int Length = sizeof(A)/sizeof(A[0]);
Node * T;
T = Establish_Tree(A,Length); //建立二叉搜索树
Inorder_Tree_Walk(T); //中序遍历输出
printf("4 Successor is %d\n",Tree_Successor(T,4));//后继函数
printf("7 Predecessor is %d\n",Tree_Predecessor(T,7));//前驱函数
printf("_____________________________________________\n");
T = Tree_Delete(T,3); //删除节点函数
Inorder_Tree_Walk(T);
return 0;
}