数据结构之二叉排序树

来源:互联网 发布:淘宝 国际米兰球衣 编辑:程序博客网 时间:2024/05/17 07:28

    小目录

         1.前言

         2.二叉排序树的查找

          3.二叉排序树的插入

          4.二叉排序树的删除

          5.二叉排序树的查找分析


                      

             1.前言

        前面的一篇博客介绍了静态查找(链接),接下来将介绍动态查找。对动态查找经常进行的操作有:(1)查找某个特定的数据元素中是否存在查找表中;(2)检索某个特定元素的各种属性(3)在查找表插入一个元素;(4)在查找表中删去某个特定元素。

      二叉排序树(Binary Sort Tree)或者是一棵空树;或者是具有下列性质的二叉树:

  1. 若左子树不空,则左子树上所有结点的值均小于根结点的值;若右子树不空,则右子树上所有结点的值均大于根结点的值。
  2. 左右子树也都是二叉排序树。
      由图可以看出,对二叉排序树进行中序遍历,便可得到一个按关键码有序的序列,因此,一个无序序列,可通过构一棵二叉排序树而成为有序序列。

                                           

   二叉排序树的数据存储结构如下:

typedef int TElemType;//二叉查找树的节点结构typedef struct BiTNode {TElemType data;struct BiTNode *lchild;struct BiTNode *rchild;}*BiTree;



   二叉排序树的查找:

  从其定义可见,二叉排序树的查找过程为:

  1. 若查找树为空,查找失败。
  2. 查找树非空,将给定值key 与查找树的根结点关键码比较。
  3. 若相等,查找成功,结束查找过程,否则,
    a.当给key 小于根结点关键码,查找将在以左孩子为根的子树上继续进行,转①
    b.当给key 大于根结点关键码,查找将在以右孩子为根的子树上继续进行,转①

 其实现算法如下:

/** @description:递归查找二叉查找树是否存在否值*/BiTree SearchBST(BiTree T,TElemType key) {    if(!T || T->data == key)        return T;    else if(T->data > key)        return SearchBST(T->lchild,key);    else        return SearchBST(T->rchild,key);}

      

     3.二叉排序树的插入

    向二叉排序树中插入一个结点的过程:设待插入结点的关键码为key,为将其插入,先要在二叉排序树中进行查找,若查找成功,按二叉排序树定义,待插入结点已存在,不用插入;查找不成功时,则插入之。因此,新插入结点一定是作为叶子结点添加上去的。

   如记录的关键码序列为:63,90,70,55,67,42,98,83,10,45,58,则构造一棵二叉排序树的过程如下:

                      

其实现算法如下:

/** @description:在二叉排序树中插入不存在的元素*/Status InsertBST(BiTree *T,TElemType key) {BiTree p,s;//二叉排序中不存在要插入的节点值if(!Search_BST(*T,key,NULL,&p)) {s = (BiTree) malloc(sizeof(struct BiTNode));if(!s)exit(OVERFLOW);s->data = key;s->lchild = NULL;s->rchild = NULL;//如果为根节点if(!p)*T = s;//小于当前值的则作为左孩子else if(key < p->data)p->lchild = s;//大于当前值的则作为右孩子else p->rchild = s;return TRUE;}elsereturn FALSE;}/** @description:查找算法判断查找是否成功,主要是配合插入操作* @more:*p指向当前节点,f直线父亲节点,查找成功则返回TRUE,否则返回FALSE*/Status Search_BST(BiTree T,TElemType key,BiTree f,BiTree *p) {if(!T) {*p = f;return FALSE;}//查找成功else if(T->data == key) {*p = T;return TRUE;}//小于当前节点的值则往左子树找else if(T->data > key ) return Search_BST(T->lchild,key,T,p);//大于当前节点值则往右子树找else return Search_BST(T->rchild,key,T,p);}


      4.二叉排序树的删除

        从二叉排序树中删除一个结点之后,使其仍能保持二叉排序树的特性即可。设待删结点为*p(p 为指向待删结点的指针),其双亲结点为*f,以下分三种情况进行讨论。

      1.*p 结点为叶结点,由于删去叶结点后不影响整棵树的特性,所以,只需将被删结点的双亲结点相应指针域改为空指针。如图9.6。

     2.*p 结点只有右子树pr 或只有左子树pl,此时,只需将pr 或pl 替换*f 结点的*p 子树即可。如图9.7。

     3.*p 结点既有左子树Pl 又有右子树Pr,可按中序遍历保持有序进行调整。

    由于第三中情况比较坑爹,下面是一张第三种情况的图,但是我觉得这里最主要的就是将中序遍历的前序和后继调整好就行。

                           

其实现算法如下:


/** @删除二叉排序树中某个节点*/Status DeleBST(BiTree *T,TElemType key) {if(!(*T))return FALSE;else {if((*T)->data == key)return Dele(T);else if((*T)->data > key)return DeleBST(&(*T)->lchild,key);else return DeleBST(&(*T)->rchild,key);}}/** @description:具体的删除操作* @more:1.如果左子树为空,则重新结它右子树;2.如果右子树为空,则重新结它的左子树3.如果左右子树都部位空,则用节点的前驱代替要删除的节点 且调整各项指针*/Status Dele(BiTree *p) {BiTree s,q;s = q = NULL;//如果左子树为空,则重新接为右子树if(! (*p)->lchild) {q = *p;*p = (*p)->rchild;free(q);}//如果右子树为空,则重新接为左子树else if(! (*p)->rchild) {q = *p;*p = (*p)->lchild;free(q);}//如果左右子树都不为空else {q = *p;s =(*p)->lchild;//向左转后,一直往右走直至尽头,为了找到p的前驱节点while(s->rchild) {q = s;s = s->rchild;}(*p)->data = s->data;//赋值//这里s节点为q的右孩子,s为p节点的前驱if(*p != q)q->rchild = s->lchild;//把s的左孩子结为q的右孩子else q->lchild = s->lchild;//重新结q的左孩子free(s);}return TRUE;}



     5.二叉排序树的查找分析

       二叉查找树中查找的运行时间与树T的高度成正比。因为有n个结点的树的高度小则为O(logn),大则为O(n).;对于有n个关键字元素的数据项,高度为h的二叉查找树T,空间复杂度为O(n);其中,查找元素,插入元素,删除元素的时间复杂度均为O(h);二叉排序树的查找过程与二分法相似,也是一个逐步缩小查找范围的过程。若查找成功,则走了一条从根结点到待查结点的路径;若失败,则是走了一条根结点到某个叶子结点的路径。因而,查找过程中和关键字的比较次数不超过树的深度。

由于含有n个结点的二叉排序树不唯一,因而有n个结点的二叉排序树的平均查找长度为树的形态有关;

     最好的情况:二叉排序树和二叉判定树形态相同;

     最坏的情况:二叉排序树为单支树,这时平均查找长度与顺序查找时相同;

      就平均性能而言,二叉排序树上的查找与二分查找相差不大,且二叉排序树上的插入和删除结点十分方便,不用大量移动结点。


以上

最后附上完整源码地址:GitHub


0 0