1、二叉排序树简介:二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),也称二叉搜索树。
树中的任意节点上的左子树都比父节点小,右子树都比父节点大的,且所有值都唯一的树叫二叉排序树。
2、二叉排序树的查找:
如果当前节点的键值等于待查询的数值,则找到;
若前节点的键值小于待查询的数值,由二叉树的定义可知,大的数值在右子树,则递归查找该节点的右子树。
若前节点的键值大于待查询的数值,小的数值在该节点的左子树,则递归查找该节点的左子树。
时间复杂度:O(Log2n)到O(n)之间
3、二叉查找树的声名与创建:
我先将待插入数值存在数组中,再将数组中的数值依次添加到二叉排序树中,按照排序树的性质创建树
typedef struct tree //二叉排序树与二叉树声名相似
{
int nval;
struct tree *pleft;
struct tree *pright;
}bst;
bst *createbst(int arr[],int length);
void addnode(bst **ptree,int num);
bst *createbst(int arr[],int length)
{
int i;
if(arr==NULL||length<=0) return NULL;
bst *ptree = NULL;
for(i=0;i<length;i++)
{
addnode(&ptree,arr[i]);
}
return ptree;
}
void addnode(bst **ptree,int num)
{
bst *ptmp = (bst*)malloc(sizeof(bst));
ptmp->nval = num;
ptmp->pleft = NULL;
ptmp->pright = NULL;
if(*ptree == NULL)
{
*ptree = ptmp;
return ;
}
bst *pnode = *ptree;
while(pnode)
{
if((pnode->nval) > num) //当查询数值小于节点键值
{
if((pnode->pleft) == NULL)
{
pnode->pleft = ptmp; //加入左子树
return ;
}
pnode = pnode->pleft;
}
else if((pnode->nval) < num) //当查询数值大于节点键值
{
if(pnode->pright == NULL)
{
pnode->pright = ptmp; //加入右子树
return ;
}
pnode = pnode->pright;
}
else if(pnode->nval == num) //如果该键值已出现过,删除
{
printf("%d这个数字已经有了\n",num);
free(ptmp);
ptmp = NULL;
return ;
}
}
}
4、二叉排序树的删除:
如果感觉建树添加节点容易的话,那删除就稍显麻烦了。要想在树中删除节点,我们就要想到该节点的父亲和儿子同不同意。。。所以考虑以下三种情况:
(1)删除节点为叶子节点即终端节点,没有儿子的情况下,可以就直接删除了,也不会破坏整个树结构;
(2)删除节点只有一个儿子(左子或右子),那么删除后就要考虑到儿子的死活(因为这关系到整个树的死活。。。),由于只有一个儿子,所以该节点的删除不影响 它父亲与它儿子的大小关系。所以直接将删除节点的儿子继承到删除节点的父亲下;这是就要知道删除节点是父亲的左儿子还是右儿子,然后直接将指针指向儿子即可。
简而言之:删除结点的左右孩子有一个为空,那么将不空的那个孩子代替要删的结点即可。
(3)删除节点有左右双子,由于有左右双子二个节点,所以直接接到删除节点的父亲上不可行。所以要找到一个适合的值替换,也就是和删除节点值最相近的节点。根据性质,我们知道删除节点的左边都是比他小的,而在这些众多比他小的节点中,最右端的节点一定是这些节点中最大的,而这个最大的值又不大于删除节点的右儿子,所以这是最佳替换的节点。以此规则类推,我们也可以将右子树的最左端节点赋给删除节点(所有比删除节点大的节点中最小的那个),使得新节点的左子树都小于它,右子树都大于它。最后,别忘了删除那个最小或最大节点。
简而言之:删除结点的左右孩子都不为空,那么找到这个结点的右(左)子树中的最小结点也就是最左(右)节点(此结点的左/右孩子一定为空),将这个最小结点的值赋给要删除的结点,然后删除这个最小结点。(找直接前驱)
void findnode(bst *ptree,bst **pdel,bst **pFather,int nNum);
void deletenode(bst **ptree,int nNum);
void findnode(bst *ptree,bst **pdel,bst **pFather,int nNum)
{
//找到删除节点和删除节点的父亲
while(ptree)
{
if(ptree->nval == nNum)
{
*pdel = ptree;
return ;
}
else if(ptree->nval > nNum)
{
*pFather = ptree;
ptree = ptree->pleft;
}
else
{
*pFather = ptree;
ptree = ptree->pright;
}
}
*pFather = NULL;
}
void deletenode(bst **ptree,int nNum) //删除节点函数
{
bst *pdel = NULL;
bst *pFather = NULL;
findnode(*ptree,&pdel,&pFather,nNum);
if(pdel == NULL) return; //未找到
bst *pMark = NULL;
if(pdel->pleft != NULL && pdel->pright != NULL) //双子
{
pMark = pdel;
pFather =pdel;
pdel = pdel->pleft; //找左子树
while(pdel->pright != NULL) //找左子树最右节点
{
pFather = pdel;
pdel = pdel->pright;
}
pMark->nval = pdel->nval; //替换节点
}
if(pFather == NULL)
{
*ptree = pdel->pleft?pdel->pleft:pdel->pright;
free(pdel);
return ;
}
if(pdel == pFather->pleft) //单子
{
pFather->pleft = pdel->pleft?pdel->pleft:pdel->pright;
}
else
{
pFather->pright = pdel->pleft?pdel->pleft:pdel->pright;
}
free(pdel); //无子直接删除
pdel = NULL;
}
测试:
int main()
{
int arr[] = {16,2,88,56,23,9,49,100,45};
bst *ptree = NULL;
ptree = createbst(arr,sizeof(arr)/sizeof(arr[0]));
printf("二叉排序树为:");
midtravel(ptree);
printf("\n");
deletenode(&ptree,88);
printf("删除节点后的二叉排序树为:");
midtravel(ptree);
printf("\n");
return 0;
}