分类目录:《算法设计与分析》总目录
相关文章:
· 二叉搜索树(一):基础知识
· 二叉搜索树(三):插入和删除
我们经常需要查找一个存储在二叉搜索树中的关键字。除了查找操作之外,二叉搜索树还能支持诸如最大元素、最小元素、前驱和后继等查询操作。本文将讨论这些操作,并且说明在任何高度为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)时间内完成。