[数据结构]查找(二)

时间:2020-12-28 14:43:22

第九章 查找

9.2  动态查找表

一、二叉排序树(二叉查找树)

1.定义:

二叉排序树或者是一棵空树;或者是具有如下特性的二叉树:

(1)若它的左子树不空,则左子树上所有结点的值均小于根结点的值;

(2)若它的右子树不空,则右子树上所有结点的值均大于根结点的值;

(3)它的左、右子树也都分别是二叉排序树。

 [数据结构]查找(二)

通常,取二叉链表作为二叉排序树的存储结构

typedef struct BiTNode { // 结点结构

  TElemType      data;

  struct BiTNode  *lchild, *rchild; // 左右孩子指针

} BiTNode, *BiTree;

2.二叉排序树的查找算法:

若二叉排序树为空,则查找不成功;否则,

1)若给定值等于根结点的关键字,则查找成功;

2)若给定值小于根结点的关键字,则继续在左子树上进行查找;

3)若给定值大于根结点的关键字,则继续在右子树上进行查找。

 [数据结构]查找(二)

从上述查找过程可见,

在查找过程中,生成了一条查找路径:

从根结点出发,沿着左分支或右分支逐层向下直至关键字等于给定值的结点;

——查找成功

从根结点出发,沿着左分支或右分支逐层向下直至指针指向空树为止。

——查找不成功

算法描述如下:

Status SearchBST (BiTree T, KeyType key,  BiTree f, BiTree &p ) {

  // 在根指针 T 所指二叉排序树中递归地查找其

  // 关键字等于 key 的数据元素,若查找成功,

  // 则返回指针 p 指向该数据元素的结点,并返回

  // 函数值为 TRUE;  否则表明查找不成功,返回

  // 指针 p 指向查找路径*问的最后一个结点,

  // 并返回函数值为FALSE, 指针 f 指向当前访问

  // 的结点的双亲,其初始调用值为NULL

} // SearchBST

if (!T)

{ p = f;  return FALSE; }  // 查找不成功

else  if ( EQ(key, T->data.key) )

    { p = T;  return TRUE; }  // 查找成功

else  if ( LT(key, T->data.key) )

     SearchBST (T->lchild, key, T, p );  // 在左子树中继续查找

else  SearchBST (T->rchild, key, T, p ); // 在右子树中继续查找

 [数据结构]查找(二)

3.二叉排序树的插入算法

·根据动态查找表的定义,“插入”操作在查找不成功时才进行;

·若二叉排序树为空树,则新插入的结点为新的根结点;否则,新插入的结点必为一个新的叶子结点,其插入位置由查找过程得到。

Status Insert BST(BiTree &T, ElemType e )

{

  // 当二叉排序树中不存在关键字等于 e.key 的

  // 数据元素时,插入元素值为 e 的结点,并返

  // 回 TRUE; 否则,不进行插入并返回FALSE

   if (!SearchBST ( T, e.key, NULL, p ))

     {                             }

   else return FALSE;

} // Insert BST

s = (BiTree) malloc (sizeof (BiTNode));// 为新结点分配空间

s->data = e;  

s->lchild = s->rchild = NULL;  

if  ( !p )  T = s;     // 插入 s 为新的根结点

else   if ( LT(e.key, p->data.key) )

       p->lchild = s;       // 插入 *s 为 *p 的左孩子

else  p->rchild = s;     // 插入 *s 为 *p 的右孩子

return TRUE;     // 插入成功

4.二叉排序树的删除算法

 和插入相反,删除在查找成功之后进行,并且要求在删除二叉排序树上某个结点之后,仍然保持二叉排序树的特性。

可分三种情况讨论:

(1)被删除的结点是叶子;

(2)被删除的结点只有左子树或者只有右子树;

(3)被删除的结点既有左子树,也有右子树。

(1)被删除的结点是叶子结点

 [数据结构]查找(二)

(2)被删除的结点只有左子树或者只有右子树

 [数据结构]查找(二)

(3)被删除的结点既有左子树,也有右子树

 [数据结构]查找(二)

算法描述如下:

Status DeleteBST (BiTree &T,  KeyType key ) {

  // 若二叉排序树 T 中存在其关键字等于 key 的

  // 数据元素,则删除该数据元素结点,并返回

  // 函数值 TRUE,否则返回函数值 FALSE

  if (!T)  return FALSE;

      // 不存在关键字等于key的数据元素

  else {                       }

} // DeleteBST

if ( EQ (key, T->data.key) )

{  Delete (T);   return TRUE;  }

 // 找到关键字等于key的数据元素

else if ( LT (key, T->data.key) )

  DeleteBST ( T->lchild, key );

            // 继续在左子树中进行查找

elseD  eleteBST ( T->rchild, key );

   // 继续在右子树中进行查找

其中删除操作过程如下所描述:

void Delete ( BiTree &p ){

   // 从二叉排序树中删除结点 p,

   // 并重接它的左子树或右子树

   if (!p->rchild)  {               }

   else if (!p->lchild) {                }

   else {              }

} // Delete

// 右子树为空树则只需重接它的左子树

q = p;  p = p->lchild;  free(q);

 [数据结构]查找(二)

 // 右子树为空树则只需重接它的左子树

q = p;  p = p->lchild;  free(q);

 [数据结构]查找(二)

// 左右子树均不空

q = p;  s = p->lchild;

while (s->rchild) { q = s;  s = s->rchild; }

                       // s 指向被删结点的前驱

p->data = s->data;

if (q != p )  q->rchild = s->lchild;             

else  q->lchild = s->lchild;

                         // 重接*q的左子树

free(s);

 [数据结构]查找(二)

5.查找性能的分析

   对于每一棵特定的二叉排序树,均可按照平均查找长度的定义来求它的 ASL 值,显然,由值相同的 n 个关键字,构造所得的不同形态的各棵二叉排序树的平均查找长 度的值不同,甚至可能差别很大。

例如:

由关键字序列 1,2,3,4,5构造而得的二叉排序树,ASL =(1+2+3+4+5)/ 5= 3

 [数据结构]查找(二)

由关键字序列 3,1,2,5,4构造而得的二叉排序树,ASL =(1+2+3+2+3)/ 5  = 2.2

 [数据结构]查找(二)

下面讨论平均情况:

    不失一般性,假设长度为 n 的序列中有 k 个关键字小于第一个关键字,则必有 n-k-1 个关键字大于第一个关键字,由它构造的二叉排序树:

 [数据结构]查找(二)

的平均查找长度是 n 和 k 的函数P(n, k)      ( 0< =k<=n-1 )。

 

 假设 n 个关键字可能出现的 n! 种排列的可能性相同,则含 n 个关键字的二叉排序树的平均查找长度:[数据结构]查找(二)在等概率查找的情况下,[数据结构]查找(二)

由此

 [数据结构]查找(二)[数据结构]查找(二)

可类似于解差分方程,此递归方程有解:[数据结构]查找(二)

二、二叉平衡树

   二叉平衡树是二叉查找树的另一种形式,其特点为:    树中每个结点的左、右子树深度之差的绝对值不大于1  

 [数据结构]查找(二)

结点的平衡因子:该结点的左子树的深度减去

    ( -1   0    1 )          右子树的深度

构造二叉平衡(查找)树的方法是:在插入过程中,采用平衡旋转技术。

   假设由于在二叉排序树上插入结点而失去平衡的最小子树根结点为A,沿插入

路径取直接下两层的结点为B和C,如果这三个结点处于一条直线上,则采用单旋转进行平衡化;如果这三个结点处于一条折线线上,则采用双旋转进行平衡化。失去平衡后进行调整的规律可归纳为下列4种情况:

(1)单向右旋平衡处理:(LLR)

原因:在A 的左子树根结点的左子树上插入结点,A的平衡因子由1增至2,至使以A为根的子树失去平衡。

调整方法:以结点B为旋转轴,将结点A向右旋转成为B的右子树,结点B代替原来A的位置,原来B的右子树成为A的左子树。

 [数据结构]查找(二)

(2)单向左旋平衡处理:(RRL)

原因:在A 的右子树根结点的右子树上插入结点,A的平衡因子由-1增至-2,至使以A为根的子树失去平衡。

调整方法:以结点B为旋转轴,将结点A向左旋转成为B的左子树,结点B代替原来A的位置,原来B的左子树成为A的右子树。

 [数据结构]查找(二)

(3)先左后右平衡处理:(LRLR)

 原因:在A 的左子树根结点的右子树上插入结点,A的平衡因子由1增至2,至使以A为根的子树失去平衡。

调整方法:先以C为旋转轴,将结点B向左旋转成为C的左子树,结点C代替原来B的位置,原来C的左子树成为B的右子树。再以C为旋转轴,将A向右旋转成为C的右子树,结点C代替原来A的位置,原来C的右子树成为B的左子树。

 [数据结构]查找(二)

(4)先右后左平衡处理:(RLRL)

 原因:在A 的右子树根结点的左子树上插入结点,A的平衡因子由-1增至-2,至使以A为根的子树失去平衡。

调整方法:先以C为旋转轴,将结点B向右旋转成为C的右子树,结点C代替原来B的位置,原来C的右子树成为B的左子树。再以C为旋转轴,将A向左旋转成为C的左子树,结点C代替原来A的位置,原来C的左子树成为B的右子树。

 [数据结构]查找(二)

平衡树的查找性能分析:

在平衡树上进行查找的过程和二叉排序树相同,因此,查找过程中和给定值进行比较的关键字的个数不超过平衡 树的深度。

问:含 n 个关键字的二叉平衡树可能达到的最大深度是多少?

先看几个具体情况:

 [数据结构]查找(二)

反过来问,深度为 h 的二叉平衡树中所含结点的最小值 Nh 是多少?

 [数据结构]查找(二)

由此推得,深度为 h 的二叉平衡树中所含结点的最小值 Nh = jh+2/5 - 1。

反之,含有 n 个结点的二叉平衡树能达到的最大深度 hn = logj(Ö5 (n+1)) - 2。

因此,在二叉平衡树上进行查找时,查找过程中和给定值进行比较的关键字的个数和 log(n) 相当。