第7章 查找——动态查找

二叉搜索树

二叉搜索树的概念

二叉搜索树(二叉排序树  二叉查找树)

根结点值大于其左子树中的所有结点值

根结点值小于其右子树中的所有结点值

左右子树都是二叉搜索树(递归)

typedef struct{
     KeyType key; … ;  
} ElemType;
typedef struct BiTNode{
    ElemType data; 
    struct BiTNode  *lchild,*rchild;
}BiTNode, *BiTree;
typedef BiTree BSTree; 
typedef BiTNode BSTNode; 

二叉搜索树的查找

递归

如果树为空,查找失败,返回NULL

否则,从根节点开始,将x与根节点比较

若x小于根节点,递归查找左子树,并返回查找结果

若x大于根节点,递归查找右子树,并返回查找结果

若x等于根节点,查找成功,返回根节点地址

BSTree BSTSearch( BSTree T, KeyType key) {
    if( !T) return NULL;   //查找失败

    else{
        if( key > T->data.key )//右子树中递归查找并返回结果

            return BSTSearch( T->rChild, key);     
        else if( key < T->data.key)//左子树递归查找并返回结果

            return BSTSearch( T->lchild, key);       
        else                          // 查找成功
            return T;                      
     }
}

非递归

p最初指向根结点,只要p不空且p所指结点不是所求则:根据比较结果令p变为当前结点的左孩子或右孩子。如此重复至p空或者找到

BiTNode *BSTSearch(BSTree T, KeyType key){
   BiTNode *p=T;
   while(p && !EQ(key,p->data.key)){
       if(LT(key,p->data.key)) 
              p=p->lchild; //向左子树中移动,继续查找
       else
              p=p->rchild;//向右子树中移动,继续查找
   }
   if(!p)return NULL;
   else return p;//查找结束

}

查找最小和最大元素

二叉检索树的插入

原理

先查找是否有重复节点,无重复则插入,插入位置是最后所走节点的孩子

代码实现

查找过程中同时记录所走节点和其父节点f, 无重复时新开辟节点放到f孩子上

递归关系:与根节点比较,相等则不插入,否则,转换为左或右子树中的插入

递归边界:发现重复不递归,返回ERROR;到达空树不递归,直接开辟节点

注意:空树时插入如何判断?

Status BSTInsert (BSTree &T, ElemType e){ //注意T需要为引用型参数

   BSTNode *p=T, *f=NULL;
   while(p!=NULL && !EQ(e.key,p->data.key)){
       if(LT(e.key, p->data.key)){  f=p;  p=p->lchild; }
       else{  f=p;  p=p->rchild; }
   }
   if(!p){ 
          q=new BiTNode;
          q->data = e; q->lchild = NULL; q->rchild = NULL;
          if(!f)T=q;
          elise if (e < f->data) f->lchild=q;
          else f->richild = q;
          return OK; 
   }
   else   return  ERROR;
   }
}

二叉搜索树的创建【补充】

原理

从空树开始,逐个读取节点并插入,直至读到结束符。

代码实现

Status CreateBST(BSTree &T){
    T = NULL;
    while( InputElem(e)<= 0){ 
        if( BSTInsert (T,e) == ERROR )
              return ERROR;
        else 
               InputElem(e);
    }
}

删除节点过程

原理:

1.删除叶子节点:直接删除

2.删除只有一个子树的节点:直接将子树接到被删除节点的双亲指针上

3.删除有两个子树节点:将左子树最大或右子树最小移至删除位置,归结为删除左子树最大或者右子树最小节点,这两类节点至多只有一个孩子!

代码实现

1.定位被删除节点及其双亲节点,以便修改双亲节点的指针取值 找不到被删除节点则无需删除,直接返回

    p=T;
    f=NULL;
   while(p!=NULL&&!EQ(key,p->data.key)){
       if(LT(key,p->data.key)){
               f=p;
               p=p->lchild;  
       }
       else{
               f=p; p=p->rchild;  
       }
   }
   if(!p) return;

 2.删除叶节点:释放节点,双亲指针赋空 双亲为空时,除释放节点外,因删除后为空树,T也需赋空

if( !p->lchild&&!p->rchild ){
   if(!f){
         free(p);   p=NULL;
         T=NULL; 
   }else if(f->lchild==p){
          free(p); p=NULL; 
          f->lchild=NULL; 
   } else {
          free(p); p=NULL;
          f->rchild=NULL;
    }
}

3.删除单子节点:释放节点,双亲指针指向其唯一的孩子 双亲为空时,除释放节点外,T也需修改 此代码同样可以用来删除叶子节点!

if(!p->lchild){//只有右子树(左子树为空)的情况
   if(!f) {
          T=p->rchild;  
          free(p);  p=NULL; 
    }else if(f->lchild==p){
           f->lchild = p->rchild; 
           free(p); p=NULL; 
   }else {
           f->rchild = p->rchild; 
           free(p);p=NULL; 
   }
}
……//只有左子树时,类似处理

4.汇总

void DeleteBST(BiTree &T,KeyType key){
 //首先定位被删结点及其双亲, 不存在则直接返回
 p=T; f=NULL;
 while(p!=NULL&&!EQ(key,p->data.key)){
   if(LT(key,p->data.key)){f=p;p=p->lchild;}else{f=p; p=p->rchild;}
 } 
 if(!p) return;
if(p->lchild&&p->rchild){//若被删节点有两个孩子
   f=p; q=p->lchild;  //f始终指向q的双亲,方便后面的删除
   while(q->rchild){f=q; q=q->rchild;} //q定位最大,f指向其双亲
   p->data=q->data;  //左子树最大者的值填充到原本被删节点
   if(f->lchild==q) f->lchild = q->lchild; else f->rchild=q->lchild;   
   free(q);
}
else if(!p->lchild){//若左孩子为空(包括只有右孩子或两个孩子都没有的情况)
   if(!f) {free(p); T=p->rchild;}
   else if(f->rchild==p){f->rchild=p->rchild; free(p);}
   else {f->lchild=p->rchild; free(p);}
}
else   {…} //只有左孩子,仿照上一段代码, 绿色部分换成p->lchild
} //教材算法9.8没用f和p,而是通过引用传递让p直接表示双亲指针,代码简化

总结与推广

什么样的二叉搜索树查找性能最高?

平衡二叉树 AVL树

概念

任意节点的平衡因子(左右子树深度之差)的绝对值不超过1。

平衡二叉搜索树的构造

先按照二叉搜索树的节点插入方法插入节点,不满足平衡要求时调整!

找到最小不平衡二叉树的根后,根据不平衡种类作相应的旋转!既要保证平衡,也要保证是二叉搜索树!

A这边边比较长 

 

 例子 

 

第一个有问题的结点选三个,中间大的往上抬。

断开的左边往左边走,右边往右边走。

插入

删除

B-树(B-Tree)

概念

 “平衡”的“多路”“查找”树( 平衡二叉树的推广) 特点:平衡(叶子节点/失败节点同层) ,多关键字,查找树

一颗m阶的B树是空树,或是满足下列特性的m叉树:

n为关键字个数

树上有序,结点也有序。

B-树的查找

从根结点出发,沿树形结构搜索结点和在结点内进行顺序(或折半)查找 两个过程交叉进行。

B-树的查找性能分析

B-树的查找时间主要花在树结点的定位上,这取决于B-树的深度。

B-树的插入(创建)

若查找成功则不能插入,否则,应试图插入到查找路径中最后一个内部结点上。内部结点关键字个数须在Ceil(m/2)-1和m-1之间,若超界则“分裂” 分裂:选中间关键字上移,当前结点分成两个,下层相应分;若上移导致双亲关键字个数超界则继续上移.

 

B-树的删除

被删节点为最下层非叶节点则直接删,否则用右侧子树最小记录顶替(规约) 内部节点关键字数量有下界Ceil(m/2)-1,低于此值要“求助”或“合并”

B+树(plus)

概念: “平衡”的“多路”“索引”树

特点:平衡(叶子节点/记录节点同层), 多关键字, 索引树, 顺序与随机查找并行

总结与推广

 

 补充:索引顺序表的查找(分块查找)

概念

对数据进行分块,使得块内无序、块间有序。将各块中的最大关键字构成一个索引表,表中还要包含每块的起始地址。

补充:分块查找过程

① 对索引表用折半查找定位最后一个小于等于key的块

② 确定了待查关键字所在的块后,块内采用顺序查找

补充:分块查找性能分析

分块查找优缺点

总结

二叉树 

 

二叉排序树

 平衡二叉树

红黑树(不大量做旋转)

B-树(树的深度变大的情况)

逐渐提高查找速率 

二叉树可视网站

https://www.cs.usfca.edu/~galles/visualization/BTree.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值