二叉搜索树/二叉排序树/二叉查找树

什么是二叉搜索树
二叉搜索树(BST,Binary Search Tree), 也称二叉排序树或二叉查找树 。一棵二叉树,可以为空;如果不为空,满足以下性质:

  1. 非空左子树的所有键值小于其根结点的键值(/关键字)。
  2. 非空右子树的所有键值大于其根结点的键值
  3. 左、右子树都是二叉搜索树
  • 注意:二叉排序树中没有相同关键字的结点。对二叉搜索树进行中序遍历可以得到按关键字排序的有序序列。*
//在讨论二叉排序树上的运算之前,定义其节点的类型如下:
typedef int keyType
typedef struct node         //记录类型
{ KeyType key;              //关键字项
InfoType data;             //其他数据域
struct node *lchild,*rchild;   //左右孩子指针
}
BSTNode;

二叉搜索树的指定结点的查找
顾名思义,这种二叉树就是用来查找的。
因为二叉排序树可看做是一个有序表,所以在二叉排序树上进行查找,和二分查找类似,也是一个逐步缩小查找范围的过程。
查找从根结点开始,如果树为空,返回NULL 。
若搜索树非空,则根结点关键字和X进行比较,并进行不同处理:
若X小于根结点键值,只需在左子树中继续搜索;如果X大于根结点的键值,在右子树中进行继续搜索; 若两者比较结果是相等,搜索完成,返回指向此结点的指针。

//递归
BSTNode *SearchBST(BSTNode *bt,KeyType k)
{  if(bt==NULL || bt->key==k)         //递归终结条件
return bt;
if (k<bt->key)
return SearchBST(bt->lchild,k); //在左子树中递归查找
else return SearchBST(bt->rchild,k); //在右子树中递归查找
}

由于非递归函数的执行效率高,可将“尾递归”函数改为迭代函数

//非递归
BSTNode *SearchBST1(BSTNode *bt,KeyType k)
{ 
while (bt!=NULL)
{
if (k==bt->key)
   return bt;
else if (k<bt->key)
   bt=bt->lchild;  //在左子树中递归查找
else
   bt=bt->rchild;  //在左子树中递归查找
}
return NULL;     //没有找到返回NULL
}

注:查找的效率决定于树的高度

查找最大和最小元素
按照性质,最大元素一定是在树的最右分枝的端结点上 ;最小元素一定是在树的最左分枝的端结点上 。参考代码如下,按英文意思进行理解即可:

//查找最小元素的递归函数 
Position FindMin( BinTree BST ) 
{     
if( !BST ) 
return NULL; /*空的二叉搜索树,返回NULL*/ 
else if( !BST->Left ) return BST;  /*找到最左叶结点并返回*/     
else  return FindMin( BST->Left ); /*沿左分支继续查找*/ } 

//查找最大元素的迭代函数 
Position FindMax( BinTree BST ) 
{     
if(BST)           
while( BST->Right )  
BST = BST->Right;         /*沿右分支继续查找,直到最右叶结点*/ 
return BST; 
}    

二叉搜索树的插入
关键是要找到元素应该插入的位置。
插入过程:
(1)若二叉排序树T为空,则创建一个key域为k的节点,将它作为根节点;
(2)否则将k和根节点的关键字比较,若两者相等,则说明树中已有此关键字k,无须插入,直接返回0;
(3)若kkey,则将k插入根节点的左子树中。
(4)否则将它插入右子树

int InsertBST(BSTNode *&p,KeyType k)     //在以*p为根节点的BST中插入一个关键字为k的节点。插入成功返回1,否则返回0
{  
if(p==NULL)             //原树为空, 新插入的记录为根节点
{   
p=(BSTNode*)malloc(sizeof(BSTNode));
p->key=k;
p->lchild=p->rchild=NULL;
return 1;
}
else if  (k==p->key)               //存在相同关键字的节点,返回0
return 0;
else if (k<p->key) return InsertBST(p->lchild,k); //插入到左子树中
else  return InsertBST(p->rchild,k);             //插入到右子树中
 }

二叉搜索树的生成
二叉排序树的生成,是从一个空树开始,每插入一个关键字,就调用一次插入算法将它插入到当前已生成的二叉排序树中。

BSTNode *CreatBST(KeyType A[],int n) //返回树根指针
{ 
BSTNode *bt=NULL;    //初始时bt为空树
int i=0;
while (i<n) 
{  
InsertBST(bt,A[i]);  //将A[i]插入二叉排序树T中
i++;
}
return bt;         //返回建立的二叉排序树的根指针
 }

任何节点插入到二叉排序树时,都是以叶子节点插入的。

**二叉搜索树的删除 **
这个过程是最复杂的。
1.①被删除的节点是叶子节点:直接删去该节点。其双亲节点中相应指针域的值改为“空”
②被删除的节点只有左子树或者只有右子树,用其左子树或者右子树代替它。其双亲节点的相应指针域的值改为 “指向被删除节点的左子树或右子树”。
③被删除的节点既有左子树,也有右子树:以其前驱替代之,然后再删除该前驱节点。前驱是左子树中最大的节点

int DeleteBST(BSTNode *&bt,KeyType k)          //在bt中删除关键字为k的节点
{  
if(bt==NULL) return 0;   //空树删除失败
else 
{
if (k<bt->key) 
return DeleteBST(bt->lchild,k);//递归在左子树中删除为k的节点
else if (k>bt->key) return DeleteBST(bt->rchild,k);//递归在右子树中删除为k的节点
else 
{  
Delete(bt);    //调用Delete(bt)函数删除*bt节点
return 1;
}
}}

void Delete(BSTNode *&p)    //从二叉排序树中删除*p节点
{ 
BSTNode *q;
if(p->rchild==NULL)      //*p节点没有右子树的情况
{   
q=p; 
p=p->lchild;   //其左子树的根节点放在被删节点的位置上
free(q);  
}
else if (p->lchild==NULL)    //*p节点没有左子树
{   
q=p; 
p=p->rchild;                //其右子树的根节点放在被删节点的位置
free(q);  
}
else Delete1(p,p->lchild);    //*p节点既有左子树又有右子树的情况
}

void Delete1(BSTNode *p,BSTNode *&r) //当被删*p节点有左右子树时的删除过程
{  
BSTNode *q;
if (r->rchild!=NULL) Delete1(p,r->rchild);      //递归找最右下节点
else     //找到了最右下节点*r
{   
p->key=r->key;            //将*r的关键字值赋给*p
q=r; 
r=r->lchild;                   //将左子树的根节点放在被删节点的位置上
free(q); //释放原*r的空间
}
 }

2.另一种类似做法:
考虑三种情况: 
①要删除的是叶结点:直接删除,并再修改其父结点指针—置为NULL ;
在这里插入图片描述

②要删除的结点只有一个孩子结点: 将其父结点的指针指向要删除结点的孩子结点 ;
在这里插入图片描述

③要删除的结点有左、右两棵子树: 用另一结点替代被删除结点:右子树的最小元素 或者 左子树的最大元素 ;取右子树中的最小元素替代 ,取左子树中的最大元素替代 ;
在这里插入图片描述

BinTree Delete( ElementType X, BinTree BST )  
{   
Position Tmp;      
if( !BST ) 
printf("要删除的元素未找到");      
else if( X < BST->Data )              
BST->Left = Delete( X, BST->Left); /* 左子树递归删除 */     
else if( X > BST->Data )              
BST->Right = Delete( X, BST->Right); /* 右子树递归删除 */     
else /*找到要删除的结点 */           
if( BST->Left && BST->Right ) 
{ /*被删除结点有左右两个子结点 */               
Tmp = FindMin( BST->Right );                 /*在右子树中找最小的元素填充删除结点*/              
BST->Data = Tmp->Data;               
BST->Right = Delete( BST->Data, BST->Right);         /*在删除结点的右子树中删除最小元素*/          
} 
else { /*被删除结点有一个或无子结点*/              
Tmp = BST;               
if( !BST->Left ) /* 有右孩子或无子结点*/                   
BST = BST->Right;               
else if( !BST->Right ) /*有左孩子或无子结点*/                   
BST = BST->Left;              
free( Tmp );          
}     
return BST; 
} 

二叉查找树结构由于树的深度过大而造成磁盘I/O读写过于频繁,进而导致查询效率低下.那么如何提高效率,即如何避免磁盘过于频繁的多次查找呢?根据磁盘查找存取的次数往往由树的高度所决定,所以,只要我们通过某种较好的树结构减少树的结构尽量减少树的高度。这样的树就是:平衡二叉树

各种树
平衡二叉树
B-和B+树的定义、性质特点、举例说明
线索二叉树
哈夫曼树Huffman Tree

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:深蓝海洋 设计师:CSDN官方博客 返回首页
评论

打赏作者

further_

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值