二叉搜索树(BinarySortTree),又称二叉查找树、二叉排序树。它或者是一棵空树;或者是具有下列性质的二叉树:
1.若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2.若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3.左、右子树也分别为二叉排序树。若子树为空,查找不成功。
(本文对二叉搜索树的删除作详细解释)
代码实现:
头文件:
BSTree.h
#pragma once
#include<assert.h>
#include<malloc.h>
#include<stdio.h>
#include<stdlib.h>
typedef int Datatype;
typedef struct BSTreeNODE
{
struct BSTreeNODE* pleft;
struct BSTreeNODE* pright;
Datatype data;
}Node, *PNode;
void Init(PNode *proot); //初始化
PNode BuyNode(Datatype data); //申请新节点
int Insert(PNode* proot, Datatype data); //插入
void Inorder(PNode proot); //中序遍历(二叉搜索树中序遍历结果有序)
PNode Find(PNode proot, Datatype data); //查找
int Delete(PNode *proot, int data); //删除
void DestroyBSTree(PNode* proot); //销毁
void Test();
源文件:
BSTree.c
void Init(PNode *proot)
{
assert(proot);
*proot = NULL;
}
PNode BuyNode(Datatype data)
{
PNode pnewnode = (PNode)malloc(sizeof(Node));
assert(pnewnode);
pnewnode->data = data;
pnewnode->pleft = NULL;
pnewnode->pright = NULL;
return pnewnode;
}
void Inorder(PNode proot)
{
if (proot)
{
Inorder(proot->pleft);
printf("%d ", proot->data);
Inorder(proot->pright);
}
}
int Insert(PNode *proot, Datatype data)//不允许插入相同关键字
{
PNode pcur = NULL;
PNode parent = NULL;
assert(proot);
if (NULL == *proot)
*proot = BuyNode(data);
//找位置
pcur = *proot;
while (pcur)
{
if (data < pcur->data)
{
parent = pcur;
pcur = pcur->pleft;
}
else if (data > pcur->data)
{
parent = pcur;
pcur = pcur->pright;
}
else
return 0;
}
pcur = BuyNode(data);
if (data < parent->data)
parent->pleft = pcur;
else
parent->pright = pcur;
return 1;
}
PNode Find(PNode proot, Datatype data)
{
PNode pcur = proot;
while (pcur)
{
if (data == pcur->data)
return pcur;
else if (data < pcur->data)
pcur = pcur->pleft;
else
pcur = pcur->pright;
}
return NULL;
}
删除可分为四种情况
1.搜索树为空,直接返回
2.搜索树没有右孩子(包括有左孩子的节点和叶子节点)
①根节点:5号节点
部分代码:
pdel = pcur;
if (pcur == *proot)
{
pdel = *proot;
*proot = pcur->pleft;
}
②非根节点:如1号和4号节点
部分代码:
if (pcur == parent->pleft)
parent->pleft = pcur->pleft;//1
else
parent->pright = pcur->pleft;//4
3.没有左孩子(包括有右孩子的节点和空节点)
①根节点:5号节点
部分代码:
pdel = pcur;
if (pcur == *proot)
{
pdel = *proot;
*proot = pcur->pright;
}
②非根节点:6号和8号节点
部分代码:
if (parent->pleft = pcur) //6
{
parent->pleft = pcur->pright;
}
else
parent->pright = pcur->pright;//8
4.有左右孩子
思路:找到结点p的中序直接前驱结点s,把结点s的数据转移到结点p,然后删除结点s,由于结点s为p的左子树中最右的结点,因而s无右子树,所以接下来方法和第二种情况类似。
部分代码:
parent = pcur;
pdel = pcur->pleft;
while (pdel->pright)
{
parent = pdel;
pdel = pdel->pright;
}
pcur->data = pdel->data;
if (pcur == parent)
{
parent->pleft = pdel->pleft;
}
else
{
parent->pright = pdel->pleft;
}
删除部分总代码:
int Delete(PNode *proot, int data)
{
assert(proot);
PNode pcur = NULL;
PNode pdel = NULL;
PNode parent = NULL;
if (NULL == *proot)
return 0;
pcur = *proot;
while (pcur)
{
if (data == pcur->data)
break;
else if (data < pcur->data)
{
parent = pcur;
pcur = pcur->pleft;
}
else
{
parent = pcur;
pcur = pcur->pright;
}
}
if (NULL == pcur)
{
return 0;
}
//只有左孩子 || 叶子节点
if (NULL == pcur->pright)
{
pdel = pcur;
if (pcur == *proot)
{
pdel = *proot;
*proot = pcur->pleft;
}
else
{
if (pcur == parent->pleft)
parent->pleft = pcur->pleft;//1
else
parent->pright = pcur->pleft;//4
}
}
//只有右孩子 || 叶子节点
else if (NULL == pcur->pleft)
{
pdel = pcur;
if (pcur == *proot)
{
pdel = *proot;
*proot = pcur->pright;
}
else
{
if (parent->pleft = pcur) //6
{
parent->pleft = pcur->pright;
}
else
parent->pright = pcur->pright;//8
}
}
else
{
parent = pcur;
pdel = pcur->pleft;
while (pdel->pright)
{
parent = pdel;
pdel = pdel->pright;
}
pcur->data = pdel->data;
if (pcur == parent)
{
parent->pleft = pdel->pleft;
}
else
{
parent->pright = pdel->pleft;
}
}
free(pdel);
return 1;
}
销毁 , 测试代码:
void DestroyBSTree(PNode* proot)
{
assert(proot);
free(proot);
proot = NULL;
}
void Test()
{
int a[] = { 5,3,4,1,7,8,2,6,0,9 };
int i = 0;
PNode proot = NULL;
Init(&proot);
for (; i < sizeof(a) / sizeof(a[0]); i++)
{
Insert(&proot, a[i]);
}
Inorder(proot);
}