1.二叉排序树
二叉排序树又称二叉查找数,它或者是一棵空树,或者是具有下列性质的二叉树
1.若它的左子树不为空,则左子树上所有结点的值均小于它的根结构的值
2.若它的右子树不为空,则右子树上所有结点的值均大于它的根结构的值
3.它的左右子树也分别为二叉排序树(递归)
比如现在有一个无序序列
想要生成一个二叉排序树,首先将70 作为根节点
将105作为70 的右子树,
115>70,115>105,作为105的右子树
104>70,104<105,作为105的左子树
依次类推:
2.二叉排序树的查找
利用递归的方式,如果key 大于当前结点,则继续查找当前结点的右子树,如果小于,则继续查找当前结点的左子树。如果最后没有找到,则查找失败。
typedef int Status;
#define OK 1
#define ERROR 0
typedef struct BiTNode
{
int data;
struct BiTNode *lchild;
struct BiTNode *rchild;
}BiTNode,*BiTree;
// 递归查找二叉排序树 T 中是否存在 key
// 指针 f 指向 T 的双亲,其初始值调用值为 NULL
// 若查找成功,则指针 p 指向该数据元素结点,并返回 OK
// 否则指针 p 指向查找路径上访问的最后一个结点,并返回 ERROR
Status SearchBiTree(BiTree T, int key,BiTree f,BiTree *p)
{
//空树
if (!T)
{
//说明没有查到,返回访问路径上的最后一个节点,也即是T的双亲
*p = f;
return ERROR;
}
else if (T->data == key)
{
//找到了
*p = T;//成功则p指向查找到的节点
return OK;
}
else if (T->data > key)
{
return SearchBiTree(T->lchild, key, f, p);//在左子树继续查找
}
else
return SearchBiTree(T->rchild, key, f, p);//在右子树继续查找
}
3.二叉排序树的插入
首先判断是否已经存在该元素,如果已经存在则插入操作失败,否则插入。
利用二叉排序树的查找操作,如果最后没有找到该元素,由于传入了一个指针指向找到的元素结点(找到时)或者查找路径上的最后一个结点(没有找到时),所以如果没有找到,就将待插元素与查找路径上的最后一个结点的值进行比较,小于则作为该结点的左子树,大于则作为该结点的右子树。
// 当二叉排序树 T 中不存在关键字等于 key 的数据元素时,
// 插入 key 并返回 OK,否则返回 ERROR
Status InsertBST(BiTree *T, int key)
{
//首先查找是否已经存在该元素
BiTree p;
Status s = SearchBiTree(*T, key, NULL, &p);
if (s == OK)
{
//该二叉有序树中已经存在该元素!不再插入
return ERROR;
}
//不存在要插入的元素,则插入,
else
{
BiTNode *r = (BiTNode*)malloc(sizeof(BiTNode));
r->data = key;
r->lchild = NULL;
r->rchild = NULL;
//p为空说明为空树,因为在查找函数中,T为空,将T的双亲f赋给p,而在传参时f=NULL;
if (!p)
{
*T = r;//插入s为新的根节点
}
else if (key > p->data)
{
p->rchild = r; //插入s为右孩子
}
else
{
p->lchild = r; //插入s为左孩子
}
return OK;
}
}
4.二叉排序树的删除
二叉排序树的删除操作比较麻烦,首先要找的待删元素,如果找不到,则不删除任何元素,如果找到了,有三4种情况
1.叶子结点
比如我们要找的是46,直接删除即可
2.只有左子树
如果我么要删除67,67只有左子树,我们将67的左子树接到67的双亲,再讲67删除即可
3.只有右子树
4.既有左子树又有右子树
比如上图我们想删除105,与只有左子树类似,只需将105的左子树接到105的双亲,删除105即可。
还是删除105,但是105现在既有左子树又有右子树,直接将左子树或右子树接到105的双亲都不对,你把左子树接到双亲,右子树怎么办,同样的你把右子树接到双亲左子树怎么办。
这里需要提到二叉排序树的一个特点,二叉排序树用中序遍历是有序的,如上图中序遍历的结果是 46,67,70,99,100,103,104,105,108,110,112,115。
解决二叉树删除问题的第一种方式,只要将待删除结点的数据替换为其直接前驱。
并将直接前驱的左子树(一定是左子树,否则该点一定不是直接前驱,可以回想一下中序遍历的过程,左-根-右,不明白可以给104加一个右子树)接到双亲,删除该直接前驱结点。
还有一种情况就是其直接后继就是其左子树,也就是其左子树不存在右子树
此时只需要将直接后继的左子树接为待删数据的左子树即可
第二种方式是将该结点的数据替换为直接后继。
将直接后继的右子树(一定为右子树,否则该节点不是直接后继,不明白可以给108加左子树,用中序遍历走一遍)接到直接后继的双亲,删除直接后继结点
还有一种情况与用直接前驱一样,其直接后继结点就是其右子树
此时将右子树的右子树接为待删数据结点的右子树
代码如下:
Status DeleteBST(BiTree *T, int key)
{
//空树
if (!*T)
{
return ERROR;
}
else
{
if (key == (*T)->data)
{
return Delete(T);
}
//继续寻找该节点
else if (key < (*T)->data)
{
return DeleteBST(&(*T)->lchild, key);
}
else
return DeleteBST(&(*T)->rchild, key);
}
}
Status Delete(BiTree *T)
{
BiTree q, s;
//叶子结点的情况包含在只有左子树和只有右子树的情况里了
//只有左子树
if ((*T)->rchild == NULL)
{
q = *T;
*T = (*T)->lchild;
free(q);
}
//只有右子树
else if ((*T)->lchild == NULL)
{
q = *T;
*T = (*T)->rchild;
free(q);
}
//有左右子树
//该方法是利用直接前驱替换
else
{
//让q=直接前驱的双亲
q = *T;
//寻找待删结点的直接前驱
s = (*T)->lchild;
while (s->rchild)
{
q = s;
s = s->rchild;
}
//将待删数据替换为直接前驱的数据
(*T)->data = s->data;
if (q != *T)
{
//将直接前驱的左子树接到直接前驱的双亲
q->rchild = s->lchild;
}
else
{
//如果直接前驱就是待删数据结点的左子树
q->lchild = s->lchild;
}
//释放直接前驱
free(s);
}
////该方法利用直接后继
//else
//{
// //让q=直接后继的双亲
// q = *T;
// s = (*T)->rchild;
// //寻找直接后继
// while (s->lchild)
// {
// q = s;
// s = s->lchild;
// }
// //将数据替换为直接后继
// (*T)->data = s->data;
// if (q != *T)
// {
// //将直接后继的右子树接到直接后继的双亲的左子树
// q->lchild = s->rchild;
// }
// else
// {
// //如果直接后继就是其右子树,将直接后继的右子树接到待删结点的右子树
// q->rchild = s->rchild;
// }
// //释放直接后继
// free(s);
//}
return OK;
}