• 算法导论读书笔记(12)


    算法导论读书笔记(12)

    二叉查找树

    如下图所示,一棵二叉查找树是按二叉树结构来组织的。这样的树可以用链表结构来表示,其中每一个结点都是一个对象。结点中除了 key 域和卫星数据外,还包含域 leftrightp ,它们分别指向结点的左儿子,右儿子和父结点。如果某个儿子结点或父结点不存在,则相应域中的值为 NIL 。根结点是树中唯一的父结点为 NIL 的结点。

    在二叉查找树(binary search tree)上执行的基本操作的时间与树的高度成正比。对于一棵含 n 个结点的完全二叉树,这些操作的最坏情况运行时间为 Θ ( lg n )。但是,如果树是含 n 个结点的线性链,则这些操作的最坏情况运行时间为 Θ ( n )。

    二叉查找树中关键字的存储方式总是满足以下的 二叉查找树 性质:

    x 为二叉查找树中的一个结点。如果 yx 的左子树中的一个结点,则 y.key <= x.key 。如果 yx 的右子树中的一个结点,则 y.key >= x.key

    根据二叉查找树的性质,可以用一个递归算法按排列顺序输出树中的所有关键字。这种算法称为 中序遍历算法 ,因为子树根的关键字在输出时介于左子树和右子树的关键字之间(类似地, 前序遍历 中根的关键字在其左右子树的关键字之前输出,而 后序遍历 中根的关键字在其左右子树的关键字之后输出)。

    INORDER-TREE-WALK(x)
    1 if x != NIL
    2     INORDER-TREE-WALK(x.left)
    3     print x.key
    4     INORDER-TREE-WALK(x.right)
    

    定理
    如果 x 是一棵包含 n 个结点的子树的根,则调用 INORDER-TREE-WALK(x) 过程的时间为 Θ ( n )。

    查询二叉查找树

    对于二叉查找树,最常见的查找操作除了 SEARCH 外,二叉查找树还支持 MINIMUMMAXIMUMSUCCESSORPREDECESSOR 等查询。对高度为 h 的树,它们都可以在 O ( h )时间内完成。

    查找

    下面的过程在树中查找一个给定的关键字。

    TREE-SEARCH(x, k)
    1 if x == NIL or k == x.key
    2     return x
    3 if k < x.key
    4     return TREE-SEARCH(x.left, k)
    5 else
    6     return TREE-SEARCH(x.right, k)
    

    该过程从树的根结点开始进行查找,并沿树下降。对碰到的每个结点 x ,就比较 kx.key 。如果这两个关键字相同,则查找结束。如果 k 小于 x.key ,则继续查找 x 的左子树,如果 k 大于 x.key ,则继续在 x 的右子树中查找。也可以用 while 循环来替代递归过程。

    ITERATIVE-TREE-SEARCH(x, k)
    1 while x != NIL and k != x.key
    2     if k < x.key
    3         x = x.left
    4     else
    5         x = x.right
    6 return x
    

    最大关键字元素和最小关键字元素

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

    TREE-MINIMUM(x)
    1 while x.left != NIL
    2     x = x.left
    3 return x
    

    在以 x 为根的子树中,最小关键字可以在 x.left 为根的左子树中找到。过程 TREE-MAXIMUM 的伪码是对称的:

    TREE-MAXIMUM(x)
    1 while x.right != NIL
    2     x = x.right
    3 return x
    

    对高度为 h 的树,这两个过程的运行时间都是 O ( h )。

    前趋和后继

    给定一个二叉查找树中的结点,有时要求找出其在中序遍历顺序下它的后继。如果所有的关键字均不相同,则某一结点 x 的后继即具有大于 x.key 中的关键字中最小者的那个结点。

    TREE-SUCCESSOR(x)
    1 if x.right != NIL
    2     return TREE-MINIMUM(x.right)
    3 y = x.p
    4 while y != NIL and x == y.right
    5     x = y
    6     y = y.p
    7 return y
    

    TREE-SUCCESSOR 代码中包含两种情况。

    • 如果结点 x 的右子树非空,则 x 的后继即右子树中的最左结点,可以通过 TREE-MINIMUM(x.right) 过程找到。
    • 如果结点 x 的右子树为空,且 x 有一个后继 y ,则 yx 的最低祖先结点,且 y 的左儿子也是 x 的祖先。

    如下图所示,包含关键字15的结点的后继是包含关键字17的结点。包含关键字13的结点的后继是包含关键字15的结点。

    过程 TREE-PREDECESSORTREE-SUCCESSOR 对称,其运行时间都是 O ( h )。

    TREE-PREDECESSOR(x)
    1 if x.left != NIL
    2     return TREE-MAXIMUM(x.left)
    3 y = x.p
    4 while y != NIL and x == y.left
    5     x = y
    6     y = y.p
    7 return y
    

    插入和删除

    插入和删除操作会引起二叉树结构上的变化,这时,就要修改其数据结构,以保持二叉查找树性质。

    插入

    为将一个新值 v 插入到二叉查找树 T 中,可以调用 TREE-INSERT 。传给该过程的参数是个结点 z ,并且有 z.key = vz.left = NILz.right = NIL

    TREE-INSERT(T, z)
    1  y = NIL
    2  x = T.root
    3  while x != NIL
    4      y = x
    5      if z.key < x.key
    6          x = x.left
    7      else
    8          x = x.right
    9  z.p = y
    10 if y == NIL
    11     T.root = z    // tree T was empty
    12 elseif z.key < y.key
    13     y.left = z
    14 else
    15     y.right = z
    

    TREE-INSERT 过程从根结点开始,并沿树下降。指针 x 跟踪了这条路径,而 y 始终指向 x 的父结点。过程根据 z.keyx.key 的比较结果,决定向左或向右转。这到 x 成为 NIL 为止。这个 NIL 所占位置即我们想插入项 z 的地方。

    删除

    将结点 z 从二叉查找树 T 中删除共有如下三种情况,其中一种有一点难懂。

    • 如果结点 z 没有孩子结点,我们就可以直接使用 NIL 来代替该结点。
    • 如果结点 z 只有一个子女,可以直接用结点 z 的孩子结点替代 z
    • 如果结点 z 有两个子女,首先要找到结点 z 的后继 y (它一定在 z 的右子树中),并用 y 替代 z 在树中的位置。

    TREE-DELETE 过程用于从二叉查找树 T 中删除一个给定的结点 z ,它按如下四种情况组织代码。

    • 如果结点 z 没有左孩子,那么我们用右孩子替换 z ,其中右孩子可能为 NIL 。右孩子为 NIL 即结点 z 没有孩子结点的情况。
    • 如果结点 z 有且只有左孩子,那么就用左孩子替代结点 z
    • 否则,结点 z 有两个子结点。首先找到 z 的后继结点 y ,它位于 z 的右子树且没有左孩子。这里我们打算用 y 替换 z
      • 如果 yz 的右孩子,那么直接用 y 替换 z
      • 否则,用 y 自己的右孩子替换 y ,再用 y 来替换 z

    此外还有一个子程序 TRANSPLANT ,用于子树之间的替换。

    TRANSPLANT(T, u, v)
    1 if u.p == NIL
    2     T.root = v
    3 elseif u == u.p.left
    4     u.p.left = v
    5 else
    6     u.p.right = v
    7 if v != NIL
    8     v.p = u.p
    
    TREE-DELETE(T, z)
    1  if z.left == NIL
    2      TRANSPLANT(T, z, z.right)
    3  elseif z.right == NIL
    4      TRANSPLANT(T, z, z.left)
    5  else
    6      y = TREE-MINIMUM(z.right)
    7      if y.p != z
    8          TRANSPLANT(T, y, y.right)
    9          y.right = z.right
    10         y.right.p = y
    11     TRANSPLANT(T, z, y)
    12     y.left = z.left
    13     y.left.p = y
    
  • 相关阅读:
    Java Class的field如果以小写t开头
    ACM集训日志——day1——15.7.8
    分金币 Uva 11300
    RMQ小结
    Poj 水题
    Codeforces Round #278 (Div. 2)
    poj 3685
    poj 3579
    vConsole
    js更换自定义鼠标指针图片
  • 原文地址:https://www.cnblogs.com/sungoshawk/p/3722604.html
Copyright © 2020-2023  润新知