定义:只要这棵树中每个节点的左孩子都小于这个节点,右孩子都大于这个节点,那么这棵树就是一个二叉搜索树。
性质
(01) 若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(02) 任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(03) 任意节点的左、右子树也分别为二叉查找树;
(04) 没有键值相等的节点(no duplicate nodes);
首先给出头文件
#ifndef __BSTREE_H__
#define __BSTREE_H__
#include<stdio.h>
#include<assert.h>
#include<malloc.h>
typedef int DataType;
typedef struct BSTreeNode
{
DataType data;
struct BSTreeNode *Left;
struct BSTreeNode *Right;
}BSTreeNode ,*PBSTreeNode;
//初始化
void BSTreeNodeInit(PBSTreeNode* pp);
//插入
void InsertBSTreeNode(PBSTreeNode* pp,DataType data);
//获得一个新的节点
PBSTreeNode BuyBSTreeNode(DataType data);
//删除搜索二叉树节点
void DeleteBSTreeNode(PBSTreeNode * pp,DataType data);
//递归法查找节点
int FindBSTree(PBSTreeNode pp,DataType data);
//递归法插入
void RecInsertBSTree(PBSTreeNode* pp,DataType data);
//递归法删除
void RecDeleteBSTree(PBSTreeNode* pp,DataType data);
#endif //__BSTREE_H__
搜索二叉树的创建,我们直接通过给初始化好的树节点指针域插入即可
所以我们先来初始化一个树节点指针域
//初始化
void BSTreeNodeInit(PBSTreeNode* pp)
{
assert(pp);
*pp=NULL;
}
要插入节点就必须要创建一个节点
//获得一个新的节点
PBSTreeNode BuyBSTreeNode(DataType data)
{
PBSTreeNode ret;
ret=(PBSTreeNode)malloc(sizeof(BSTreeNode));
if(ret==NULL)
{
return NULL;
}
ret->data=data;
ret->Left=NULL;
ret->Right=NULL;
return ret;
}
下面只要进行从插入就可以了,只不过有有一定规则
实际就是搜索二叉树的性质,大于根节点进入右子树,小于根节点进入左子树 ,来看看代码
//插入
void InsertBSTreeNode(PBSTreeNode * pp,DataType data)
{
PBSTreeNode parent;
PBSTreeNode pCur;
assert(pp);
if(*pp==NULL)//空树,直接赋给根指针
{
*pp =BuyBSTreeNode(data);
}
pCur=*pp;
while(pCur)//寻找插入点
{
if(data>pCur->data)
{
parent=pCur;//将将要插入的节点的双亲节点标记起来
pCur=pCur->Right;
}
else if (data<pCur->data)
{
parent=pCur; //同上
pCur=pCur->Left;
}
else //找到了
{
return ;
}
}
if(data>parent->data)//判断将要插入的节点是左子树还是右子树
{
parent->Right=BuyBSTreeNode(data);
}
else
{
parent->Left=BuyBSTreeNode(data);
}
}
通过不断地插入,就可以创建一个搜索二叉树
然后还必须实现删除这棵树的节点的功能
分析删除节点可能遇到的情况
情况1:根节点 情况1:不是根
情况2:根节点 情况2:不是根节点
情况3:不是根节点 情况3:是根节点
情况4:左右孩子都在
下面来看看代码
//删除搜索二叉树节点
void DeleteBSTreeNode(PBSTreeNode * pp,DataType data)
{
PBSTreeNode pCur;
PBSTreeNode parent;
PBSTreeNode pDel;
assert(pp);
pCur=*pp;
while(pCur)//找到待删除的节点
{
if(data>pCur->data)
{
parent=pCur;
pCur=pCur->Right;
}
else if (data<pCur->data)
{
parent=pCur;
pCur=pCur->Left;
}
else
{
break ;
}
}
if(pCur==NULL)//说明就没有该节点
{
return ;
}
//右孩子为空,包含两种情况,叶子结点和只有左孩子
if(pCur->Right==NULL)
{
if(pCur==*pp)//是根节点
{
*pp=pCur->Left;
}
else//不是根节点
{
//是父亲节点的左孩子
if(parent->Left==pCur)
{
parent->Left=pCur->Left;
}
//是父亲节点的右孩子
else
{
parent->Right=pCur->Left;
}
}
}
//只有右孩子,
else if (pCur->Left==NULL)
{
if(pCur==*pp)//是根节点
{
*pp=pCur->Right;
}
else//不是根节点
{
//是父亲节点的左孩子
if(parent->Left==pCur)
{
parent->Left=pCur->Right;
}
//是父亲节点的右孩子
else
{
parent->Right=pCur->Right;
}
}
}
else
{
//左右孩子都存在,方法是找到一个替代的节点,将该节点的值交给待删节点,后删掉替代节点
pDel=pCur->Left;
parent=pCur;//处理不能进入循环的情况
while(pDel->Right)//寻找待删节点的左子树中最大值,也就是一直顺着右找
{
parent=pCur;
pCur=pCur->Right;
}
pCur->data=pDel->data;//将最大值交给待删节点
if(pDel==parent->Left)//是父亲节点的左孩子
{
parent->Left=pDel->Left;
}
else//是父亲节点的右孩子
{
parent->Right=pDel->Left;
}
pCur=pDel;
}
free(pCur);
}
下面再来说说递归法解决这些问题
第一个是递归法查找节点是否存在
个人觉得这是一种单线的递归,而遍历二叉树那种遍历是双线的递归
//递归法查找节点
int FindBSTree(PBSTreeNode pp,DataType data)
{
if(pp==NULL)
{
return 0;
}
if(data==pp->data)
{
return 1;
}
if(data>pp->data)
{
return FindBSTree(pp->Right,data);
}
else
{
return FindBSTree(pp->Left,data);
}
}
//递归法插入
void RecInsertBSTree(PBSTreeNode* pp,DataType data)
{
assert(pp);
if(*pp==NULL)
{
*pp=BuyBSTreeNode(data);
}
else
{
if(data<(*pp)->data)
{
RecInsertBSTree((&(*pp)->Left),data);
}
else
{
RecInsertBSTree((&(*pp)->Right),data);
}
}
}
//递归法删除
void RecDeleteBSTree(PBSTreeNode* pp,DataType data)
{
PBSTreeNode pDel;
//1,先找出待删除节点
if(*pp==NULL)
{
return ;
}
if(data>(*pp)->data)
{
RecDeleteBSTree(&(*pp)->Right,data);
return;
}
else if(data<(*pp)->data)
{
RecDeleteBSTree(&(*pp)->Left,data);
return;
}
else//进入这个条件说明找到了
{
//和之前一样,分4种情况,我们选择将叶子节点和有右孩子一起处理
pDel=*pp;
if(pDel->Left==NULL)//叶子和只有右
{
(*pp)=pDel->Right;
free(pDel);
}
else if(pDel->Right==NULL)//只有左
{
*pp=pDel->Left;
free(pDel);
}
else//左右都有
{
pDel=pDel->Left;//进入左
while(pDel->Right)//找最大值()替代节点
{
pDel=pDel->Right;
}
(*pp)->data=pDel->data;//赋值
RecDeleteBSTree(&pDel,pDel->data);//删除代替节点
return;
}
}
}