一. 什么是二叉排序树
二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。它可以是一棵空树。
特点:
- 若它的左子树不为空,则左子树上所有结点的值均小于它的根结构的值。
- 若它的右子树不为空,则右子树上所有结点的值均大于它的根结构的值。
- 它的左右子树也分别为二叉排序树。
- 二叉搜索树支持对数时间的搜索,支持对数时间级别的插入和删除。
- 一个无序序列可以通过构建一棵二叉排序树,从而变成一个有序序列。
二. 二叉排序树的结构
与二叉树相同:
typedef struct BinNode
{
int data;
struct BinNode* lchild, * rchild;
}BinNode,*BinTree;
三. 二叉排序树的建立
void CreatTree(BinTree *T)
{
char n;
printf("请输入数据:\n");
scanf("%c", &n);
if (n == '#')
{
*T = NULL;
}
else
{
*T = (BinTree)malloc(sizeof(BinNode));
if (!(*T))
{
return;
}
else
{
(*T)->data = n;
CreatTree(&(*T)->lchild);
CreatTree(&(*T)->rchild);
}
}
}
四. 二叉排序树的查找
在二叉排序树不为空树的前提下,首先将被查找值同树的根结点进行比较
- 如果相等,查找成功;
- 如果比较结果为根结点的关键字值较大,则说明该关键字可能存在其左子树中;
- 如果比较结果为根结点的关键字值较小,则说明该关键字可能存在其右子树中;
BinTree SearchTree(BinTree T, int key)
{
/*
*
if (!T)
{
return NULL;
}
else if (T->data == key)
{
return T;
}
*/
//简单写法如下
//如果递归过程中 T 为空,则查找结果,返回NULL;或者查找成功,返回指向该关键字的指针
if (!T || T->data == key)
{
return T;
}
else if (T->data > key)
{
return SearchTree(T->lchild,key);
}
else if (T->data < key)
{
return SearchTree(T->rchild, key);
}
}
五. 二叉排序树的插入
当因为查找失败而需要插入数据元素时,该数据元素的插入位置一定位于二叉排序树的叶子结点,并且一定是查找失败时访问的最后一个结点的左孩子或者右孩子。
要完成插入操作,需要更改上述查找函数:
//指针 f 指向父结点,初始化为 NULL
bool SearchBST(BiTree T,int key, BiTree f, BiTree *p)
{
// 如果 T 指针为空(找到尽头),说明查找失败,令 p 指针指向查找过程中最后一个叶子结点,并返回查找失败的信息
if (!T)
{
*p = f;
return false;
}
else if(key==T->data)
{
// 如果相等,令p指针指向该关键字,并返回查找成功信息
*p = T;
return true;
}
else if(key<T->data)
{
return SearchBST(T->lchild, key, T, p);
}
else
{
return SearchBST(T->rchild, key, T, p);
}
}
插入:
bool InsertTree(BinTree T, int key)
{
//查找成功,p指向查找到的结点
//查找失败,p指向最后一个访问的结点
BinTree p;
//查找失败
if (!SearchTree(T, key, NULL, &p))
{
BinTree s = (BinTree)malloc(sizeof(BinNode));
s->data = key;
s->lchild = s->rchild = NULL;
//若 p 不存在,则为空树,新增结点为根结点
if (!p)
{
T = s;
}
else if (key < p->data)
{
//插入s为左孩子
p->lchild = s;
}
else {
//插入s为右孩子
p->rchild = s;
}
return true;
}
//数中已有关键字相同的结点,不在插入
return false;
}
有了插入操作,就可以构建二叉排序树:
int i,n;
BinTree T = NULL;
printf("请输入你想要添加数据的数目:\n");
scanf("%d",&n);
int *a = (int*)malloc(n*sizeof(int));
printf("请依次输入你想要添加的数据:\n");
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
InsertTree(&T,a[i]);
}
也可以不依赖查找函数,直接添加数据:
void InsertTree2(BinTree T, int key)
{
//若为空树,或者未找到
if (!T)
{
T = (BinTree)malloc(sizeof(BinNode));
T->data = key;
T->lchild = T->rchild = NULL;
return;
}
//若找到则不添加
if (T->data == key)
{
return;
}
if (T->data > key)
{
InsertTree2(T->lchild,key);
}
else
{
InsertTree2(T->rchild, key);
}
}
六. 二叉排序树的删除
-
该结点只有左子树或者只有右子树,此时只需要将其左子树或者右子树直接变为结点 p 双亲结点的子树即可;
- 左孩子为空,重接右孩子 ; 右孩子为空,重接左孩子
-
该结点为叶子结点,此时只需要删除该结点,并修改其双亲结点的指针即可;
-
该结点左右子树都不为空:
- 需要找到这个要删除的结点的前驱或直接后继,把该前驱,换到该结点处。同时在二叉排序树中对其直接前驱(或直接后继)做删除操作。
- 需要找到这个要删除的结点的前驱或直接后继,把该前驱,换到该结点处。同时在二叉排序树中对其直接前驱(或直接后继)做删除操作。
int Deletetree(BinTree* T, int key)
{
//未找到key
if (!*T)
{
return false;
}
else
{
if (key < (*T)->data)
{
Deletetree(&(* T)->lchild,key);
}
else if (key > (*T)->data)
{
Deletetree(&(*T)->rchild, key);
}
else//找到,开始删除操作
{
BinTree *p = T;
BinTree q;
//左孩子为空,重接右孩子
if ((*p)->Left == NULL)
{
q = *p;
*p = (*p)->rchild;
free(q);
}
//右孩子为空,重接左孩子
else if ((*p)->rchild == NULL)
{
q = *p;
*p = (*p)->lchild;
free(q);
}
//左右孩子都不为空
else
{
q = *p;
//寻找前驱,转左,向右到尽头
BinTree s=(*p)->lchild;
while (s->rchild)
{
q = s;
s = s->rchild;
}
(*p)->data = s->data;
//此时的 s 指向被删除结点直接前驱,此时的 p 是要删除的那个结点,此时的 q 是 s 的双亲结点
if (q != *p)
{
q->rchild = s->lchild;
}
else
{
q->lchild = s->rchild;
}
free(s);
}
}
}
- 令结点的左子树为其双亲结点的左子树;结点 p 的右子树为其自身直接前驱结点的右子树
// T 为待删除的结点
void kill(BinTree* T)
{
BinTree s;
//若无子树
if (!(*T)->lchild && !(*T)->rchild)
{
*T = NULL;
}
//若无左子树,重接右子树
else if (!(*T)->lchild)
{
s = *T;
*T = (*T)->rchild;
free(s);
}
//若无右子树,重接左子树
else if (!(*T)->rchild)
{
s = *T;
*T = (*T)->lchild;
free(s);
}
//若左右子树都有
else
{
s = *T;
BinTree p = (*T)->lchild;
//找到该结点前驱(该结点左子树的最右子树)
while (p->rchild)
{
p = p->rchild;
}
//将该结点的右子树接到前驱的后面-->前驱的右子树
p->rchild = (*T)->rchild;
//将该结点的左子树变成该结点双亲的左子树
*T = (*T)->lchild;
free(s);
}
}
//找出待删除的结点
int Delete(BinTree* T, int m)
{
//若找不到待删除的结点,或树为空
if (!(*T))
{
return false;
}
//若找到待删除的结点
else if ((*T)->data == m)
{
kill(T);
return true;
}
else if ((*T)->data > m)
{
Delete(&(*T)->lchild,m);
}
else if ((*T)->data < m)
{
Delete(&(*T)->rchild, m);
}
}
七. 二叉排序树的最值
最小值:
BinTree FindMin(BinTree T)
{
if (T)
{
while (T->lchild)
{
T = T->lchild;
}
}
return T;
}
最大值:
BinTree FindMax(BinTree T)
{
if (T)
{
while (T->rchild)
{
T = T->rchild;
}
}
return T;
}
八. 二叉排序树的打印
//中序打印
void printTree(BinTree T)
{
if (T)
{
printTree(T->lchild);
printf("%d",T->data);
printTree(T->rchild);
}
}