• 《算法导论》12章二叉排序树BST


     二叉排序树(Binary Sort Tree)又称二叉查找(搜索)树。 它或者是一棵空树;或者是具有下列性质的二叉树:

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

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

    (3)左、右子树也分别为二叉排序树;

    二叉查找树中序遍历可得到一个有序序列。

    定理:如果x是一颗包含n个节点的子树的根,则调用中序遍历过程的时间为O(n).(导论上有证明)。

    (1)查找SEARCH

      在二叉查找树中查找一个给定的关键字k的过程与二分查找很类似,根据二叉查找树在的关键字存放的特征,很容易得出查找过程:首先是关键字k与树根的关键字进行比较,如果k大比根的关键字大,则在根的右子树中查找,否则在根的左子树中查找,重复此过程,直到找到与遇到空结点为止。例如下图所示的查找关键字13的过程:(查找过程每次在左右子树中做出选择,减少一半的工作量)

    书中给出了查找过程的递归和非递归形式的伪代码:

    TREE_SEARCH(x,k)
      if x=NULL or k=key[x]
          then return x
      if(k<key[x])
          then return TREE_SEARCH(left[x],k)
       else
          then return TREE_SEARCH(right[x],k)
    
    ITERATIVE_TREE_SEARCH(x,k)
      while x!=NULL and k!=key[x]
          do if k<key[x]
                  then x=left[x]
               else
                  then x=right[x]
       return x

    2)查找最大关键字元素和最小关键字元素

     要查找二叉树中具有最小关键字的元素,只要从根节点开始,沿着各节点的left指针查找下面,直到遇到NIL时为止,下面的过程返回

    以给定节点x为根的子树中最小元素的指针:

    Tree-minimum(x)

       while left[x]!=NIL

               do x<------left[x]

       return x;

    最大节点:

     1 TREE_MAXMUM(x)
    2 2    while right[x] != NULL
    3 3         do x= right[x]
    4 4     return x

    对高度为h的树,这两个过程都是O(h),这是因为,如在TRee-search 过程中一样,所遇到的节点序列构成了一条沿着根节点向下的路径


    (3)前驱和后继

      给定一个二叉查找树中的结点,找出在中序遍历顺序下某个节点的前驱和后继。如果树中所有关键字都不相同,则某一结点x的前驱就是小于key[x]的所有关键字中最大的那个结点,后继即是大于key[x]中的所有关键字中最小的那个结点。根据二叉查找树的结构和性质,不用对关键字做任何比较,就可以找到某个结点的前驱和后继


    书中给出了求x结点后继结点的伪代码:
    TREE_SUCCESSOR(x)
        if right[x] != NULL
            then return TREE_MINMUM(right(x))
        y=parent[x]
        while y!= NULL and x ==right[y]
               do x = y
                   y=parent[y]
        return y

    Tree-successor的代码中包含2种情况.如果节点x的右子树非空,则x的后继即右子树中的最左节点。

    另一种情况,正如练习12.2-6中要求读者证明的那样,如果节点x的右子树为空,且x有一个后继y,则y是x的最低祖先节点,且y的左儿子也是x的祖先。

    查找后继步骤:先判断x是否有右子树,如果有则在right[x]中查找关键字最小的结点,即使x的后继。如果没有右子树,则从x的父节点开始向上查找,直到遇到某个结点是其父结点的左儿子的结点时为止。例如下图查找结点13的后继结点15的过程:


    查找前驱步骤:先判断x是否有左子树,如果有则在left[x]中查找关键字最大的结点,即是x的前驱。如果没有左子树,则从x继续向上执行此操作,直到遇到某个结点是其父节点的右孩子结点。例如下图查找结点7的前驱结点6过程:

    或者可以看如何查找17的前驱15的。


    定理:对一棵高度为h的二叉查找,动态集合操作SEARCH、MINMUM、MAXMUM、SUCCESSOR、PROCESSOR等的运行时间均为O(h)。

    12.3插入和删除

     插入和删除会引起以二叉查找树表示的动态集合的变化,要反映出这种变化,就要修改数据结构,但在修改的同时还要保持二叉查找树性质。插入过程相当来说要简单一些,删除结点比较复杂。

    插入:

    TREE_INSERT(T,z)
        y = NULL;
        x =root[T]
        while x != NULL
            do y =x
                if key[z] < key[x]
                     then x=left[x]
                     else  x=right[x]
         parent[z] =y
         if y=NULL
            then root[T] =z //tree T was empty
            else if key[z]>key[y]
                       then  keft[y]  = z
                       else   right[y] =z

    Tree-insert从根节点出发,并沿着树下降,指针x跟踪了这条路径,而y始终指向x的父节点。在初始化后,第3-7行中的while循环

    是这两个指针下降,根据key[z]和key[x]的比较结果,可以决定是向左转还是向右转。直到x称为NIL时为止。

    这个NIL所占位置就是我们想插入项Z的地方。第8-13行设置有关z的指针。

    删除:

    删除时有三种可能:

    1) 如果z没有子女,则修改其父节点p[z], 使NIL为其子女,如图(a)

    2) 如果结点只有一个子女,则可以通过在其子结点与父节点间建立一条链来删除z,如图(b)

    3)如果结点z有两个子女,先删除z的后继y,再用y的内容来替代z的内容,如图(c)

     1 TREE_DELETE(T,z)
     2     if left[z] ==NULL or right[z] == NULL
     3        then y=z
     4        else  y=TREE_SUCCESSOR(z)
     5    if left[y] != NULL
     6        then x=left[y]
     7        else  x=right[y]
     8    if x!= NULL
     9        then parent[x] = parent[y]
    10    if p[y] ==NULL
    11       then root[T] =x
    12       else if y = left[[prarnt[y]]
    13                   then left[parent[y]] = x
    14                   else  right[parent[y]] =x
    15     if y!=z
    16         then key[z] = key[y]
    17               copy y's data into z
    18      return y

    第1-3行确定要删除的结点y,该节点是y或者是输入结点z(如果z至多只有一个节点),或者是z的后继(如果z有2个子女)。

  • 相关阅读:
    lcn 分布式事务协调者集群原理
    springboot 监控 Actuator
    springboot 配置文件说明
    docker 安装jenkins
    docker 搭建maven 私服
    docker 安装 gitlab
    docker 安装软件
    docker 部署 java 项目
    mybatis 中between and用法
    vue-router history 模式 iis 配置
  • 原文地址:https://www.cnblogs.com/youxin/p/2610954.html
Copyright © 2020-2023  润新知