算法导论读书笔记-第十二章-二分检索树

算法导论第12章 二叉搜索树

12.1 什么是二叉搜索树

二叉搜索树以二叉树的结构组织, 每个结点包含key, 卫星数据, left(指向左孩子), right(指向右孩子), p(指向双亲). 二叉搜索树满足以下性质: 对于每个结点\(x\) , 其左子树上的结点不大于\(x\) , 右子树上的结点不小于\(x\) .

中序遍历二叉搜索树, 可以按序输出树中所有关键字.

# 调用下面的过程inorder_tree_walk(T.root)可以按序输出二叉搜索树T中所有元素
inorder_tree_walk(x):
  if x is not None:
    inorder_tree_walk(x.left)
    print x.key
    inorder_tree_walk(x.right)

定理12.1 : 如果\(x\) 是一棵有n个结点子树的根, 那么调用 inorder_tree_walk(x) 需要\(\Theta(n)\) 时间.

12.2 查询二叉搜索树

# 调用下面的过程recurrsive_tree_search(x,k), 其中x为根结点, k为关键字.
# 如果关键字为k的结点存在, 返回一个指向关键字为k的结点的引用; 否则返回None
recurrsive_tree_search(x,k):
  if (x is None) or (k == x.key):
    return x
  if k < x.key:
    return tree_search(x.left)
  else:
    return tree_search(x.right)
# 调用下面的过程iterative_tree_search(x,k), 其中x为根结点, k为关键字.
# 如果关键字为k的结点存在, 返回一个指向关键字为k的结点的引用; 否则返回None
iterative_tree_search(x,k):
  while (x is not None) and (k != x.key):
    if k < x.key:
      x = x.left
    else:
      x = x.right
  return x

MAXIMUM & MINIMUM

# x为根结点
# 返回最小结点
tree_minimum(x):
  while x.left is not None:
    x = x.left
    return x
# x为根结点
# 返回最大结点
tree_maximum(x):
  while x.right is not None:
    x = x.right
    return x

以上过程在一棵高度为h的树上均能在O(h)时间内执行完.

SUCCESSOR & PREDECESSOR

# x是根结点
# 若x的后继存在, 返回其后继结点
# 若x是最大关键字, 返回None
tree_successor(x):
  # 若x的右子树非空, 则x的后继为右子树中的最小结点
  if x.right is not None: 
    return tree_minimum(x.right)
  # 若x的右子树为空, 则x是某棵树T的最大结点(最右下角)
  # 找到满足x是其最右下角的最大的子树T, 则x的结点为T.p.
  # 具体做法是一直往父结点找, 一直找到一个有左子树的根结点T.
  y = x.p
  while (y is not None) and (x == y.right):
    x = y
    y = y.p
  return y
# x是根结点
# 若x的前驱存在, 返回其前驱结点
# 若x是最小关键字, 返回None
tree_predecessor(x):
  # 若x的左子树非空, 则x的前驱为左子树中的最大结点
  if x.left is not None: 
    return tree_maximum(x.left)
  # 若x的左子树为空, 则x是某棵树T的最小结点(最左下角)
  # 找到满足x是其最左下角的最大的子树T, 则x的结点为T.p.
  # 具体做法是一直往父结点找, 一直找到一个有右子树的根结点T.
  y = x.p
  while (y is not None) and (x == y.left):
    x = y
    y = y.p
  return y

定理12.2 : 在一棵高度为h的二叉搜索树上, 动态集合上的操作SEARCH, MINIMUM, MAXIMUM, SUCCESSOR, PREDECESSOR可以在O(h)时间内完成.

12.3 插入和删除

INSERT

# T是一棵二叉搜索树(根结点)
# z是一个新结点, 其中z.key=v, z.left=None, z.right=None
tree_insert(T, z):
  # y用来找z的父节点
  y = None
  # x用来遍历
  x = T.root
  # 确定父节点
  while x is not None:
    y = x
    if z.key < x.key:
      x = x.left
    else:
      x = x.right
  # 插入
  z.p = y
  if y is None:
    T.root = z
  else if z.key < y.key:
    y.left = z
  else:
    y.right = z

DELETE

从一棵二叉搜索树T中删除一个结点z分为三种基本情况:

  • 若z没有孩子结点, 简单删除并修改其父结点.
  • 若z只有一个孩子结点, 用这个孩子代替z的位置.
  • 若z有两个孩子, 找z的后继(或前驱)y, 并让y代替z的位置.

实际算法中可以这样实现:

  • 若z没有左孩子, 则用右孩子代替z的位置. (包括了没有右孩子的情况).
  • 若z有左孩子:
    • 若z没有右孩子, 则用左孩子代替z的位置.
    • 若z有右孩子, 找出z的后继y:
      • 若y是z的右孩子, 用y替换z的位置.
      • 若y不是z的右孩子, 先用y的右孩子替换y, 再用y替换z.
# 用一棵以v为根结点的子树替换一棵以u为根结点的子树
# u的双亲变成v的双亲, v成为u的双亲的相应孩子
# 注: v可以看作是一棵新的树, 可以不是原树T的子树
transplant(T, u, v):
  # u是T的根
  if u.p is None:
    T.root = v
  # u是父结点的左孩子
  else if u == u.p.left:
    u.p.left = v
  # u是父结点的右孩子
  else:
    u.p.right = v
  if v is not None:
    v.p = u.p
# 从二叉搜索树T中删除结点z
# 流程见上面实际算法的实现
tree_delete(T, z):
  if z.left is None:
    transplant(T, z, z.right)
  else if z.right is None:
    transplant(T, z, z.left)
  else:
    y = tree_minimum(z.right)
    if y.p != z:
      transplant(T, y, y.right)
      y.right = z.right
      y.right.p = y
    transplant(T, z, y)
    y.left = z.left
    y.left.p = y

定理12.3 : 在一棵高度为h的二叉搜索树上, 实现动态集合操作INSERT和DELETE的运行时间均为O(h).

12.4 随机构建二叉搜索树

随机构建二叉搜索树: 按随机次序插入这些关键字到一棵初始的空树中而生成的树.

定理12.4 : 一棵有n个不同关键字的随机构建二叉搜索树的期望高度为O(lgn).

posted @ 2017-11-15 15:00  AyiStar  阅读(244)  评论(0编辑  收藏  举报