二叉排序树就是左孩子比根节点小,右孩子比根节点大,左小右大的那种二叉树。从定义来看,二叉排序树是不可能有俩个数是相同的。
二叉排序树是为了查找时更加有效率,查找的时候如果小,就往左边跑,一下子就去掉了整个右边。
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
typedef float datatype;
typedef struct BiTNode {
datatype data;
struct BiTNode *lchild, *rchild;
}BiTNode,*BiTree;
//===================================================
//中序遍历
void MidTraverse(BiTree tree) {
if(tree) {
MidTraverse(tree->lchild);
printf("%.1f ",tree->data);//修改数据类型的时候这里也要改
MidTraverse(tree->rchild);
}//if
}//MidTraverse
//=======================================================
//找数值是否已经在排序树中了
/*
这是在网上看到的递归写法,parent代表要找节点的父节点,
*find返回要找节点
*/
bool SearchBST(BiTree tree, datatype item, BiTree *parent, BiTree *find) {
if(!tree) {//空树
*find = *parent;
return false ;
}//if
else if(item == tree->data) {
*find = tree;//找到了节点
return true;
}//else if
else if(item < tree->data) {//找左子树
return SearchBST(tree->lchild, item, &tree, find);
}//else if
else //找右子树
return SearchBST(tree->rchild, item, &tree, find);
}//SearchBST
//================================================================
//上面函数非递归法
/*传指针是因为后面的插入删除都要用到parent find的返回值
如果找到了find就是要找的节点,否则为空
找到了parent就是find的父节点,否则parent就是叶子节点。
*/
bool SearchBST1(BiTree tree, datatype item, BiTree *parent, BiTree *find) {
*find = tree;//开始当然从根节点开始
while( (*find) && item != (*find)->data) {
*parent = *find;//parent作为find的父节点
//不要写成parent= find虽然语法上没错,但逻辑上不对
if(item < (*find)->data) //找左孩子
*find = (*find)->lchild ;
else//找右孩子
*find = (*find)->rchild;
}//while
if(!(*find)) {
printf("没找到\n");
return false;
}
else
return true;
}//SearchBST1
//=============================================================
//二叉排序树中插入节点
/*
插入一个数之前判断树中是否已经有了(二叉排序树定义就是没有相同的数)
插入树的过程就是建立二叉排序树的过程,开始建立树为空,那么第一个插入的数
就做根节点,然后接下来插入数,就同样判断是否存在,找到插入数在树中的parent,
小点就做parent左孩子,大点就做parent右孩子。原理就酱紫。
*/
bool InsertBST(BiTree *tree, datatype item) {
BiTree p = *tree;
BiTree find;
BiTree pNew;
BiTree parent = NULL ;
if(SearchBST1(p, item, &parent, &find)) {
printf("该元素已经存在!\n");
return false ;
}//if
else {
/*在判断条件里创建节点比一进函数就创要好,因为条件不满足
就不用浪费空间了,小知识吧,慢慢进步
*/
printf("进入赋值:");
pNew = (BiTree )malloc(sizeof(BiTNode ));
pNew->data = item;
pNew->lchild = pNew->rchild = NULL;
if(!*tree) {
*tree = pNew;
printf("做根节点\n");
}//if
else if(item < parent->data) {
printf("左孩子\n");
parent->lchild = pNew;
}//else if
else {
printf("右孩子\n");
parent->rchild = pNew;
}//else
printf("遍历:\n");
MidTraverse (*tree);
printf("\n");
return true;
}//else
}//InsertBST
//=========================================================
//删除节点
/*
删除节点有三种情况,一种是删除的节点没有左右孩子,光杆司令,第二种是
只有一个孩子,这俩种可以归纳为一种方式:
if(!find->lchild || !find->rchild)
假如要删除的节点a是她父节点的左孩子,那么反正a最多也就只有一个孩子,
就相当于这个唯一的孩子把a给挤走了,自己做a父节点的左孩子
而第三种接口就是要删除的节点有俩个孩子,假如我们我删除节点3,这时候解决的关键是谁来
顶替3,如果做的多的话就可以知道,我们要找的其实是按中序遍历(左中右)的
直接前驱,也就是3.5。
6
/ \
3 7
/ \ \
2 4 10
/ / \ /
1 3.6 5 8
/ \
3.5 9
\
3.7
\
3.8
*/
bool DeleteBST(BiTree tree, datatype item) {
BiTree p = tree;
BiTree parent = NULL;
BiTree find;
if(!SearchBST1(p, item, &parent, &find)) {
printf("删除的元素不在树中\n");
return false ;
}//if
if(!find->lchild || !find->rchild) {//一个或俩者都为空
if(parent->lchild == find)
parent->lchild = (find->lchild) ? find->lchild : find->rchild;
else
parent->rchild = (find->lchild) ? find->lchild : find->rchild;
free(find);
}//if
else {//有俩个孩子
BiTree temp = find->rchild;//要删除节点的右孩子
BiTree p;//直接前驱的父节点
while(temp->lchild) {
p = temp;//父节点
temp = temp->lchild;
//直接前驱也就是find的右孩子的最左节点,temp是直接前驱
}//while
find->data = temp->data;//直接前驱的值给要删除节点
//前驱肯定没有左孩子的,要不然就不是直接前驱了。
if(temp->rchild) {//如果直接前驱有右孩子
p->rchild = temp->rchild;
//直接前驱的右孩子占据前驱的位置
}//if
free(p->lchild);//看,最后释放的却不是要删除的节点空间,变相理解这里删除
p->lchild = NULL ;
// free(temp);
// temp = NULL;//这样子却不对,按道理p->lchild和temp应该一样的,还是我程序写错了?不明白求指教
}//else
}//DeleteBST
//===================================================
//测试程序
int main() {
BiTree tree = NULL;
// int a[] = {62,58,88,47,73,99,35,51,93,37};
float a[] = {6,3,7,2,4,1,5,3.6,3.5,3.8,3.7,10};
for(int i = 0 ; i < 12; i++) {
InsertBST(&tree,a[i]);
}
MidTraverse (tree);
printf("\n");
DeleteBST(tree,3);
MidTraverse (tree);
return 0;
}