算法设计与分析——二叉搜索树(二):查询二叉搜索树

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


我们经常需要查找一个存储在二叉搜索树中的关键字。除了查找操作之外,二叉搜索树还能支持诸如最大元素、最小元素、前驱和后继等查询操作。本文将讨论这些操作,并且说明在任何高度为h的二叉搜索树上,如何在 O ( h ) O(h) O(h)时间内执行完每个操作。

查找

查找我们使用下面的过程在一棵二叉搜索树中查找一个具有给定关键字的结点。输入一个指向树根的指针和一个关键字 k k k,如果这个结点存在,tree_search(x, k)返回一个指向关键字为 k k k的结点的指针;否则返回None

def tree_search(x, k):
    if x == None or k == x.key:
        return x
    if k < x.key:
        return tree_search(x.left, k)
    else:
        return tree_search(x.right, k)

这个过程从树根开始查找,并沿着这棵树中的一条简单路径向下进行。于遇到的每个结点 x x x,比较关键字 k k k x . k e y x.key x.key。如果两个关键字相等,查找就终止。如果 k < x . k e y k<x.key k<x.key,查找在 x x x的左子树中继续,因为二叉搜索树性质蕴涵了 k k k不可能被存储在右子树中。对称地,如果 k > x . k e y k>x.key k>x.key,查找在右子树中继续。从树根开始递归期间遇到的结点就形成了一条向下的简单路径,所以 tree_search(x, k)的运行时间为 O ( h ) O(h) O(h),其中 h h h是这棵树的高度。

我们也可以采用while循环来展开递归,用一种迭代方式重写这个过程。对于大多数计算机,迭代版本的效率要高得多。

def tree_search(x, k):
    while x != None or k != x.key:
        if k < x.key:
            x = x.left
        else:
            x = x.right
最大元素与最小元素

通过从树根开始沿着left孩子指针直到遇到一个None,我们总能在一棵二叉搜索树中找到一个元素。下面tree_mimimum(x)过程返回了一个指向在以给定结点 x x x为根的子树中的最小元素的指针,这里假设不为None

def tree_mimimum(x):
    while x.left != None:
        x = x.left
    return x

二叉搜索树性质保证了tree_mimimum(x)是正确的。如果结点 x x x没有左子树,那么由于 x x x右子树中的每个关键字都至少大于或等于 x . k e y x.key x.key,则以 x x x为根的子树中的最小关键字是 x . k e y x.key x.key如果结点 x x x有左子树,那么由于其右子树中没有关键字小于 x . k e y x.key x.key,且在左子树中的每个关键字不大于 x . k e y x.key x.key,则以 x x x为根的子树中的最小关键字一定在以 x . l e f t x.left x.left为根的子树中。

同理,找到最大元素的代码也是类似的:

def tree_maximum(x):
    while x.right != None:
        x = x.right
    return x
前驱和后继

给定一棵二叉搜索树中的一个结点,有时候需要按中序遍历的次序查找它的后继。如果所有的关键字互不相同,则一个结点 x x x的后继是大于 x . k e y x.key x.key的最小关键字的结点。一棵二叉搜索树的结构允许我们通过没有任何关键字的比较来确定一个结点的后继。如果后继存在,下面的过程将返回一棵二叉搜索树中的结点 x x x的后继;如果 x x x是这棵树中的最大关键字,则返回 N o n e None None

def tree_successor(x):
    if x.right != None:
        return tree_mimimum(x.right)
    y = x.p
    while y != None and x == y.right:
        x = y
        y = y.p
    return y

tree_successor(x)的代码分为两种情况:如果结点 x x x的右子树非空,那么 x x x的后继恰是 x x x右子树中的最左结点,通过第3行中的tree_mimimum(x.right)调用可以找到。另一种情况只需简单地从 x x x开始沿树而上直到遇到一个其双亲有左孩子的结点。

在一棵高度为 h h h的树上,tree_successor(x)的运行时间为 O ( h ) O(h) O(h),因为该过程或者遵从条简单路径沿树向上或者遵从简单路径沿树向下。前驱和后继过程是对称的,其运行时间也为 O ( h ) O(h) O(h)。即使关键字非全不相同,我们仍然定义任何结点x的后继和前驱为分别调用tree_successor(x)来返回结点。

所以,在一棵高度为 h h h的二又搜索树上,动态集合上的操作如查找、最小元素、最大元素、前驱和后继都可以在 O ( h ) O(h) O(h)时间内完成。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 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、付费专栏及课程。

余额充值