数据结构学习笔记:二叉排序树

何为二叉排序树

     二叉排序树(Binary Sort Tree),又称二叉搜索树。它是一棵具有以下性质的二叉树:

  • 若根结点的左子树非空,则其左子树上所有结点的值都小于它。
  • 若根结点的右子树非空,则其右子树上所有结点的值都大于它。
  • 根结点的左,右子树也分别都为二叉排序树。

    例如:

数据结构学习笔记:二叉排序树_第1张图片

二叉排序树的操作

二叉排序树的查找

        二叉排序树的结点结构定义与普通二叉树相同。

struct BSTNode {
      Data data ;
      struct BSTNode  *Lchild ;
      struct BSTNode  *Rchild ;
} ;
       当我们需要在二叉排序树中查找一个关键值时,我们可以利用二叉排序树的性质,从根结点开始,每次用关键值与当前结点比较,若关键值小于当前结点,则进入当前结点的左孩子结点,若大于则进入其右孩子结点。直到关键字匹配成功。 具体代码如下:

/*Now指向当前遍历的结点,key为关键值*/
/*若查找成功,Result指向目标结点,parent指向目标结点的双亲结点,否则parent指向最后一次遍历的结点*/
status SerchBST(struct BSTNode *Now, Data key, struct BSTNode *Result, struct BSTNode *Parent)
{
      Parent = NULL ;
      Result = NULL ;
      /*若查找不成功*/
      if(Now == NULL) {
             return FALSE ;
      }
      else if(key == Now -> data) {
             Result = Now ;
             return TRUE ;
      }
      else if(key < Now -> data) {
             Parent = Now ;
             return  SearchBST(Now -> Lchild, key, Result, parent) ;
      }
      else {
             Parent = Now ;
             return SearchBST(Now -> Rchild, key, Result, parent) ;
      }
}
        此函数为递归运行的函数,每次先检查当前结点是否为空,若为空说明未查找到关键字匹配的结点,返回FALSE。若不为空,则比较当前结点与关键字的大小,若关键字与当前结点匹配成功,则返回TRUE。若当前结点小于关键字,则进入当前结点的左孩子结点,否则进入当前结点的右孩子结点。
        例如,寻找关键字75的过程如下:

数据结构学习笔记:二叉排序树_第2张图片


二叉排序树的插入

        二叉排序树的插入操作,可以看作是在查找操作的基础上多加了一个步骤而已。

        例如,想要在查找操作的例图中插入80这个数据,则首先执行以80为关键字的查找操作,当查找到75这个结点时,由于80 > 75, 而当前结点75的右孩子域为空,则将要插入的80结点赋到75结点的右孩子域。过程如下图:

数据结构学习笔记:二叉排序树_第3张图片

        具体实现代码如下:

status InsertBST(struct BSTNode *Now, struct BSTNode *key)
{
       /*若当前结点为空,则直接插入关键字结点*/
       if(Now == NULL) {
             Now = key ;
             return TRUE ;
       }
       /*若有结点值与关键字相同,则返回FALSE,不重复插入*/
       if(Now -> data == key -> data) {
             return FALSE ;
      }
      else if(key -> data < Now -> data) {
             /*若关键字小于当前结点,且当前结点的左孩子结点为空,则插入关键字并返回TRUE*/
             if(Now -> Lchild == NULL) {
                     Now -> Lchild = key ;
                     return TURE ;
             }
             else {
                     return InsertBST(Now -> Lchild, key) ;
             }
      }
      else {
             /*若关键字大于当前结点,且当前结点的右孩子结点为空,则插入关键字并返回TRUE,*/
             if(Now -> Rchild == NULL) {
                     Now -> Rchild = key ;
                     return TURE ;
             }
             else {
                     return InsertBST(Now -> Rchild, key) ;
             }
     }
 }
        多次调用二叉排序树的插入函数,可以根据不同的关键字序列来创建一个二叉排序树。

二叉排序树的删除

        二叉排序树的删除操作也是基于查找操作的,被删除结点可能有三种情况:

  • 被删除结点是叶子结点。
  • 被删除结点只有左子树或只有右子树。
  • 被删除结点左右子树都存在。

        现在分析三种情况的结点。

  • 如果被删除结点是叶子结点,则直接将其删除,并将其双亲结点相应孩子域置空。
  • 如果被删除结点只有左子树或只有有子树,则将其删除后,还需要把它的左子树或右子树移动到被删除位置。
  • 如果被删除结点左右子树都存在,则将其删除后,还需对其左右子树做一些调整。有两种方案:1.取其左子树“最右边的结点”,即是左子树值最大的结点。2.取其右子树“最左边的结点”,即是右子树值最小的结点。

数据结构学习笔记:二叉排序树_第4张图片

       例如要删除图中的根结点57,则需要在其左右子树中找到一个结点来替代它的位置,以实现对其整体结构变动最小,而这个替代它的结点需要满足二叉排序树的性质,所以就必须找到最接近根结点的结点,也就是其左子树中最大的结点44,或其右子树中最小的结点60。删除替代后就变成:

数据结构学习笔记:二叉排序树_第5张图片

       具体实现代码如下:

Status DeleteBST(BSTNode *Root, Data key)
{
        BSTNode *temp = NULL, *temp_parent = NULL ;
        BSTNode *Result = NULL ;
        BSTNode *Parent = NULL ;
        if(SerchBST(Root, key -> data, Result, Parent) != TRUE) {
                 retrun FALSE ;
        }
        /*要删除的结点左子树为空*/
        if(Result -> Lchild == NULL) {
                 /*删除结点为根结点*/
                 if(Parent == NULL) {
                         Root = NULL ;
                         free(Result) ;
                         return TRUE ;
                 }
                 else {
                        if(Parent -> Lchild == Result) {
                                Parent -> Lchild = Result -> Rchild ;
                        }
                        else if(Parent -> Rchild == Result) {
                               Parent -> Rchild = Result -> Rchild ;
                        }
                        free(Result) ;
                        return TRUE ;
                 }
        }
        /*要删除的结点右子树为空*/
        if(Result -> Rchild == NULL) {
                 /*删除结点为根结点*/
                 if(Parent == NULL) {
                         Root = NULL ;
                         free(Result) ;
                         return TRUE ;
                 }
                 else {
                        if(Parent -> Lchild == Result) {
                                Parent -> Lchild = Result -> Lchild ;
                        }
                        else if(Parent -> Rchild == Result) {
                               Parent -> Rchild = Result -> Lchild ;
                        }
                        free(Result) ;
                        return TRUE ;
                 }
        }
        /*要删除结点左右子树都存在,采取右子树最小结点方案*/
        else {
                 /*进入要删除的结点的右子树*/
                 temp = Parent -> Rchild ;
                 temp_parent = Parent ;
                 /*循环查找右子树中最小的结点和最小结点的双亲结点*/
                 while(temp -> Lchild != NULL) {
                           temp_parent = temp ;
                           temp = temp -> Lchild ;
                 }
                 /*如果替换结点有右子树,则采用单侧子树结点的处理方式*/
                 if(temp -> Rchild != NULL) {
                          temp_parent -> Lchild = temp -> Rchild ;
                 }
                 /*如果删除结点是根结点*/
                 if(Parent == NULL) {
                          temp -> Lchild = Root -> Lchild ;
                          temp -> Rchild = Root -> Rchild ;
                          Root = temp ;
                          free(Result) ;
                          return TRUE ;
                 }
                 else {
                          temp -> Lchild = Result -> Lchild ;
                          temp -> Rchild = Result -> Rchild ;
                          if(Parent -> Lchild == Result) {
                                Parent -> Lchild = temp ;
                          }
                          else if(Parent -> Rchild == Result) {
                                Parent -> Rchild = temp ;
                          }
                          free(Result) ;
                          return TRUE ;
               }
        }
}                    


  





总结

          二叉排序树就像栈至于线性表一样,也是在普通的二叉树基础上,对其操作方式等加以限制,从而达到快速从二叉树中搜索关键数据的目的。在二叉排序树创建的过程中,序列的不同顺序会导致最后生成的树有差别。




你可能感兴趣的:(数据结构学习笔记:二叉排序树)