算法设计与分析——二叉搜索树(三):插入和删除

分类目录:《算法设计与分析》总目录
相关文章:
· 二叉搜索树(一):基础知识
· 二叉搜索树(二):查询二叉搜索树
· 二叉搜索树(三):插入和删除
· 二叉搜索树(四):随机构建二叉搜索树的期望高度


插入和删除操作会引起由二叉搜索树表示的动态集合的变化。一定要修改数据结构来反映这个变化,但修改要保持二叉搜索树性质的成立。正如后面将看到的,插入一个新结点带来的树修改要相对简单些,而删除的处理有些复杂。

插入

要将一个新值 v v v插人到一棵二叉搜索树 T T T中,需要调用过程 TREE-INSERT。该过程以结点 z z z作为输入,其中 z . k e y = v z.key=v z.key=v z . l e f t = N o n e z.left=None z.left=None z . r i g h t = N o n e z.right=None z.right=None。这个过程要修改 T T T z z z的某些属性来把 z z z插入到树中的相应位置上。

def tree_insert(T, x):
    y = None
    x = T.root
    while x != None:
        y = x
        if z.key < x.key:
            x = x.left
        else:
            x = x.right
    z.p = y
    if y == None:
        T.root = z
    elif z.key < y.key:
        y.left = z
    else:
        y.right = z

下图显示了tree_insert(T, x)是如何工作的。tree_insert(T, x)从树根开始,指针 x x x记录了一条向下的简单路径,并查找要替换的输入项 z z z N o n e None None。该过程保持遍历指针 y y y作为 x x x的双亲。初始化后,while循环使得这两个指针沿树向下移动,向左或向右移动取决于 z . k e y z.key z.key x . k e y x.key x.key的比较,直到 x x x变为 N o n e None None。这个 N o n e None None占据的位置就是输入项 z z z要放置的地方。我们需要遍历指针 y y y,这是因为找到 N o n e None None时要知道 z z z属于哪个结点。

与其他搜索树上的原始操作一样,过程`tree_insert(T, x)在一棵高度为 h h h的树上的运行时间为 O ( h ) O(h) O(h)

删除

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

  • 如果 z z z没有孩子结点,那么只是简单地将它删除,并修改它的父结点,用 N o n e None None作为孩子来替换 z z z
  • 如果 z z z只有一个孩子,那么将这个孩子提升到树中 z z z的位置上,并修改 z z z的父结点,用 z z z的孩子来替换 z z z
  • 如果 z z z有两个孩子,那么找 z z z的后继 y y y,并让 y y y占据树中 z z z的位置。 z z z的原来右子树部分成为 y y y的新的右子树,并且 z z z的左子树成为 y y y的新的左子树。

从一棵二叉搜索树 T T T中删除一个给定的结点 z z z,这个过程取指向 T T T z z z的指针作为输入参数。上述情况1和情况2较为好理解,现在我们主要来看一下第三种情况。

z z z既有一个左孩子又有一个右孩子。我们要查找 z z z的后继 y y y,这个后继位于 z z z的右子树中并且没有左孩子。现在需要将 y y y移出原来的位置进行拼接,并替换树中的 z z z如果 y y y z z z的右孩子,那么用 y y y替换 z z z,并仅留下 y y y的右孩子。
y是z的右孩子
否则, y y y位于 z z z的右子树中但并不是 z z z的右孩子。在这种情况下,先用 y y y的右孩子替换 y y y,然后再用 y y y替换 z z z
y不是z的右孩子
为了在二叉搜索树内移动子树,定义一个子过程 TRANSPLANT,它是用另一棵子树替换一棵子树并成为其双亲的孩子结点。当 TRANSPLANT用一棵以 v v v为根的子树来替换一棵以 u u u为根的子树时,结点 v v v的双亲就变为结点 v v v的双亲,并且最后 v v v成为 u u u的双亲的相应孩子。

def transplant(T, u, v):
    if u.p == None:
        T.root = v
    elif u.p.left == u:
        u.p.left = v
    else:
        u.p.right = v
    if v != None:
        v.p = u.p

第1-3行处理 u u u T T T的树根的情况。否则, u u u是其双亲的左孩子或右孩子。如果 u u u是一左孩子,则将 v v v作为其左孩子,右孩子同理。我们允许 v v v N o n e None None,如果 v v v为非 N o n e None None时,就需要更新 v . p v.p v.p。注意到,transplant(T, u, v)并没有处理 v . l e f t v.left v.left v . r i g h t v.right v.right的更新,这些更新都由transplant(T, u, v)的调用者来负责。

利用现成的transplant(T, u, v)过程,下面是从二叉搜索树T中删除结点 z z z的删除过程:

def tree_delete(T, z):
    if z.left == None:
        transplant(T, z, z.right)
    elif z.right == None:
        transplant(T, z, z.left)
    else:
        y = tree_mimimum(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

除了调用tree_mimimum(x)之外,tree_delete(T, z)的每一行,包括调用transplant(T, u, v),都只花费常数时间。因此,在一棵高度为 h h h的树上,tree_delete(T, z)的运行时间为 O ( h ) O(h) O(h)。所以,在一棵高度为h的二叉搜索树上,实现动态集合操作 插入和删的运行时间均为 O ( h ) O(h) O(h)

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
二叉搜索树,也叫二叉查找树(BST,Binary Search Tree),是一种特殊的二叉树。相对于普通的二叉树,它有一个特点:所有左子树的节点都比根节点小,所有右子树的节点都比根节点大。因此,它可以快速地进行查找、插入删除等操作。 具体来说,二叉搜索树的定义如下: - 节点的左子树中所有节点的值都小于该节点的值。 - 节点的右子树中所有节点的值都大于该节点的值。 - 左右子树也都是二叉搜索树。 如下图所示,就是一个二叉搜索树的例子: ``` 8 / \ 3 10 / \ \ 1 6 14 / \ / 4 7 13 ``` 在这个树中,每个节点都满足左子树的节点值小于该节点的值,右子树的节点值大于该节点的值。比如,节点 3 的左子树是 1 和 6,右子树是 4 和 7,都满足要求。 二叉搜索树的主要操作包括查找、插入删除。 查找操作可以通过递归或者循环实现。递归实现如下: ``` def search(root, val): if not root or root.val == val: return root if root.val > val: return search(root.left, val) else: return search(root.right, val) ``` 插入操作需要先查找到插入的位置,然后创建一个新节点插入到该位置: ``` def insert(root, val): if not root: return TreeNode(val) if root.val > val: root.left = insert(root.left, val) else: root.right = insert(root.right, val) return root ``` 删除操作比较复杂,需要考虑多种情况。如果要删除的节点只有一个子节点,直接将其子节点替换上来即可。如果要删除的节点有两个子节点,可以找到其右子树中的最小节点(或者左子树中的最大节点)来替换该节点,然后再删除该最小节点(或者最大节点)。 ``` def delete(root, val): if not root: return None if root.val == val: if not root.left: return root.right elif not root.right: return root.left else: # 找到右子树中的最小节点 p = root.right while p.left: p = p.left root.val = p.val root.right = delete(root.right, p.val) elif root.val > val: root.left = delete(root.left, val) else: root.right = delete(root.right, val) return root ``` 需要注意的是,在删除节点时,要保证删除后的树仍然是二叉搜索树

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

von Neumann

您的赞赏是我创作最大的动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值