- 二叉排序树的搜索(SearchBST)
- 二叉排序树的插入(InsertBST)
- 二叉排序树的删除(Delete)
- 中序遍历(inorder)
- 先序遍历(preorder)
什么是二叉排序树
1.二叉排序树要么是空二叉树,要么具有如下特点:
- 如果结点有左孩子,那么左孩子的值小于根结点的值
- 如果结点有右孩子,那么右孩子的值大于根结点的值
- 二叉排序树中的任意一个结点均满上述两点
- 对二叉排序树执行中序遍历得到的是一组由小到大排好的元素序列
二叉排序树有何特点
- 二叉排序树的结构类似于二分查找的判定树。比根结点大,往右边找;比根节点小,往左边找。
- 二叉排序树与二分查找的判定树本质上不同之处在于,二叉排序树是动态生成的不是静态的,每次插入都会根据当前情况,确定插入位置。
- 一个无序序列可以通过构建一棵二叉排序树,从而变成一个有序序列:对二叉排序树进行中序遍历(inorder)会得到一组由小到大已排好序的数据元素
- 一组数据元素可以构造出多种二叉排序树,不同形态的二叉排序树查找效率也不相同,形态越接近于平衡二叉树(AVL树)的查找效率越高
二叉排序树的构造
- 查找:因为二叉排序树可以看成是一个有序表,所以在二叉排序树上进行查找和折半查找类似,也是一个逐步缩小查找范围的过程。 此处我们采用递归查找。
- 无论是否查找成功,我们都将查找的最终地址返回,查找成功返回查到的元素的地址;查找失败则返回空地址NULL
- 函数参数中的
Bstree *f,Bstree **p
指的其实是同一个东西——最后查找元素的前驱结点,整个递归的过程,用f
去进入层层递归当中,找到最后一个查找节点的前驱节点(双亲结点),最后通过二级指针*p
带出函数
//查找成功返回地址,查找失败返回NULL;
//T指向当前顶点,f指向T的双亲(前驱节点),最后用*p将f带出函数的层层递归
Bstree *SearchBST(Bstree *T,dataType key,Bstree *f,Bstree **p)
{
if((!T)||T->data==key)//查找完毕,查找成功返回坐标,失败返回NULL
{
*p=f;//最后将前驱存入*p带出SearchBST函数
return T;
}
else if(T->data>key)//继续查找左子树
{
return SearchBST(T->lchild,key,T,p);
}
else
{
return SearchBST(T->rchild,key,T,p);//继续查找右子树
}
}
- 插入:此处值得一提的地方在
if(SearchBST((*T),elem,NULL,&p)==NULL)//在查找失败的情况下插入
此处调用SearchBST函数有两个作用:
① 判断当前插入的元素elem是否已经存在
② 若插入元素elem在树中不存在,此时我们的指针p
在搜索的过程中保留了查找的最后位置的前驱节点,那么此时,我们要插入到元素elem
肯定是p
的孩子,那么我们只需要判断elem
与p->data
的大小关系,判断elem
是p
的左孩子还是右孩子即可.
void InsertBST(Bstree **T,dataType elem)
{
Bstree *p=NULL;//p用于存储SearchBST最终搜索位置的前驱结点(无论搜索成功或者失败,p都将存入最后搜索位置的前一个结点)
Bstree *s;
if(SearchBST((*T),elem,NULL,&p)==NULL)//在查找失败的情况下插入
{
s=(Bstree *)malloc(sizeof(Bstree));
s->data=elem;
s->lchild=NULL;
s->rchild=NULL;
if(p==NULL)//p为空说明该二叉排序树为空树,此时插入的结点为整棵树的根结点
{
*T=s;
}
else if(p->data>elem)//p->data>elem;说明elem对应的结点应当成为前驱p的左孩子
{
p->lchild=s;
}
else if(p->data<elem)//p->data<elem;说明elem对应的结点应当成为前驱p的右孩子
{
p->rchild=s;
}
}
return;
}
- 删除:二叉排序的删除情况最为复杂:
- 当要删除的结点DelNode没有孩子时,可直接删除:
① DelNode是根节点,且只有一个结点
② DelNode是前驱节点p的左孩子
③ DelNode是前驱节点p的右孩子
1.当要删除的结点DelNode没有孩子时,可直接删除
if(!(DelNode->lchild)&&!(DelNode->rchild))
{
// [情况1] DelNode是根节点,且只有一个结点
if(!p)
{
(*T)=NULL;
}
// [情况2] DelNode是前驱节点p的左孩子
else if(DelNode->data<p->data)
{
p->lchild=NULL;
}
// [情况3] DelNode是前驱节点p的右孩子
else if(DelNode->data>p->data)
{
p->rchild=NULL;
}
free(DelNode);
return;
}
- 当要删除节点DelNode只有一个孩子时(左孩子或右孩子),其孩子直接继承DelNode的位置:
④ DelNode是根节点,有右孩子
⑤ DelNode是根节点,有左孩子
⑥ DelNode是前驱节点p的左孩子,有左孩子
⑦ DelNode是前驱节点p的右孩子,有左孩子
⑧ DelNode是前驱节点p的左孩子,有右孩子
⑨ DelNode是前驱节点p的右孩子,有右孩子
2.当要删除节点DelNode只有一个孩子时(左孩子或右孩子),其孩子,直接继承DelNode的位置
if((!DelNode->lchild)||(!DelNode->rchild))
{
if(!p)//只有一个结点,且是根节点的情况
{
// [情况4] DelNode是根节点,有右孩子
if(!DelNode->lchild)
{
(*T)=DelNode->rchild;
free(DelNode);
return;
}
// [情况5] DelNode是根节点,有左孩子
if(!DelNode->rchild)
{
(*T)=DelNode->lchild;
free(DelNode);
return;
}
}
if(!DelNode->rchild)//只有左子树时
{
// [情况6] DelNode是前驱节点p的左孩子,有左孩子
if(DelNode->data<p->data)
{
p->lchild=DelNode->lchild;
}
// [情况7] DelNode是前驱节点p的右孩子,有左孩子
else
{
p->rchild=DelNode->lchild;
}
free(DelNode);
return;
}
if(!DelNode->lchild)//只有右子树时
{
// [情况8] DelNode是前驱节点p的左孩子,有右孩子
if(DelNode->data<p->data)
{
p->lchild=DelNode->rchild;
}
// [情况9] DelNode是前驱节点p的右孩子,有右孩子
else
{
p->rchild=DelNode->rchild;
}
free(DelNode);
return;
}
}
- 当要删除的结点DelNode左右子树都存在时:
⑩
3.当要删除的结点DelNode左右子树都存在时
if(DelNode->lchild&&DelNode->rchild)
{
Bstree *s;
Bstree *q;//q存储s的前驱节点
q=DelNode;
s=DelNode->lchild;
while(s->rchild)
{
q=s;
s=s->rchild;//找到的s一定是叶子节点,即无左右孩子
}
DelNode->data=s->data;
if(q==DelNode)//说明上述while循环一次都未执行
{
//q->lchild=NULL; 考虑过于片面,未考虑到s可能存在左孩子
q->lchild=s->lchild;
}
else //说明while循环找到了最右边的s
{
//q->rchild=NULL;考虑过于片面,未考虑到s可能存在左孩子
q->rchild=s->lchild;
}
free(s);
return;
}
完整源代码:
#include <stdio.h>
#include <stdlib.h>
typedef char dataType;
typedef struct BstNode
{
dataType data;
struct BstNode *lchild;
struct BstNode *rchild;
}Bstree;
//查找成功返回地址,查找失败返回NULL;
//T指向当前顶点,f指向T的双亲(前驱节点),最后用*p将f带出函数的层层递归
Bstree *SearchBST(Bstree *T,dataType key,Bstree *f,Bstree **p)
{
if((!T)||T->data==key)//查找完毕,查找成功返回坐标,失败返回NULL
{
*p=f;//最后将前驱存入*p带出SearchBST函数
return T;
}
else if(T->data>key)//继续查找左子树
{
return SearchBST(T->lchild,key,T,p);
}
else
{
return SearchBST(T->rchild,key,T,p);//继续查找右子树
}
}
void InsertBST(Bstree **T,dataType elem)
{
Bstree *p=NULL;//p用于存储SearchBST最终搜索位置的前驱结点(无论搜索成功或者失败,p都将存入最后搜索位置的前一个结点)
Bstree *s;
if(SearchBST((*T),elem,NULL,&p)==NULL)//在查找失败的情况下插入
{
s=(Bstree *)malloc(sizeof(Bstree));
s->data=elem;
s->lchild=NULL;
s->rchild=NULL;
if(p==NULL)//p为空说明该二叉排序树为空树,此时插入的结点为整棵树的根结点
{
*T=s;
}
else if(p->data>elem)//p->data>elem;说明elem对应的结点应当成为前驱p的左孩子
{
p->lchild=s;
}
else if(p->data<elem)//p->data<elem;说明elem对应的结点应当成为前驱p的右孩子
{
p->rchild=s;
}
}
return;
}
void Delete(Bstree **T,dataType elem)
{
//先找到要删除的结点位置
Bstree *p=NULL;//p用于存储SearchBST最终搜索位置的前驱结点
Bstree *DelNode;
DelNode=SearchBST((*T),elem,NULL,&p);
if(DelNode)
{
//1.当要删除的结点DelNode没有孩子时,可直接删除
if(!(DelNode->lchild)&&!(DelNode->rchild))
{
// [情况1] DelNode是根节点,且只有一个结点
if(!p)
{
(*T)=NULL;
}
// [情况2] DelNode是前驱节点p的左孩子
else if(DelNode->data<p->data)
{
p->lchild=NULL;
}
// [情况3] DelNode是前驱节点p的右孩子
else if(DelNode->data>p->data)
{
p->rchild=NULL;
}
free(DelNode);
return;
}
//2.当要删除节点DelNode只有一个孩子时(左孩子或右孩子),其孩子,直接继承DelNode的位置
if((!DelNode->lchild)||(!DelNode->rchild))
{
if(!p)//只有一个结点,且是根节点的情况
{
// [情况4] DelNode是根节点,有右孩子
if(!DelNode->lchild)
{
(*T)=DelNode->rchild;
free(DelNode);
return;
}
// [情况5] DelNode是根节点,有左孩子
if(!DelNode->rchild)
{
(*T)=DelNode->lchild;
free(DelNode);
return;
}
}
if(!DelNode->rchild)//只有左子树时
{
// [情况6] DelNode是前驱节点p的左孩子,有左孩子
if(DelNode->data<p->data)
{
p->lchild=DelNode->lchild;
}
// [情况7] DelNode是前驱节点p的右孩子,有左孩子
else
{
p->rchild=DelNode->lchild;
}
free(DelNode);
return;
}
if(!DelNode->lchild)//只有右子树时
{
// [情况8] DelNode是前驱节点p的左孩子,有右孩子
if(DelNode->data<p->data)
{
p->lchild=DelNode->rchild;
}
// [情况9] DelNode是前驱节点p的右孩子,有右孩子
else
{
p->rchild=DelNode->rchild;
}
free(DelNode);
return;
}
}
//3.当要删除的结点DelNode左右子树都存在时
if(DelNode->lchild&&DelNode->rchild)
{
Bstree *s;
Bstree *q;//q存储s的前驱节点
q=DelNode;
s=DelNode->lchild;
while(s->rchild)
{
q=s;
s=s->rchild;//找到的s一定是叶子节点,即无左右孩子
}
DelNode->data=s->data;
if(q==DelNode)//说明上述while循环一次都未执行
{
//q->lchild=NULL; 考虑过于片面,未考虑到s可能存在左孩子
q->lchild=s->lchild;
}
else //说明while循环找到了最右边的s
{
//q->rchild=NULL;考虑过于片面,未考虑到s可能存在左孩子
q->rchild=s->lchild;
}
free(s);
return;
}
}
}
void inorder(Bstree *T)
{
if(!T)
{
return;
}
inorder(T->lchild);
printf("%c ",T->data);
inorder(T->rchild);
}
void preorder(Bstree *T)
{
if(!T)
{
return;
}
printf("%c ",T->data);
preorder(T->lchild);
preorder(T->rchild);
}
int main()
{
Bstree *T=NULL;
}