分类目录:《算法设计与分析》总目录
相关文章:
· 二叉搜索树(一):基础知识
· 二叉搜索树(二):查询二叉搜索树
· 二叉搜索树(三):插入和删除
· 二叉搜索树(四):随机构建二叉搜索树的期望高度
插入和删除操作会引起由二叉搜索树表示的动态集合的变化。一定要修改数据结构来反映这个变化,但修改要保持二叉搜索树性质的成立。正如后面将看到的,插入一个新结点带来的树修改要相对简单些,而删除的处理有些复杂。
插入
要将一个新值 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
y
y位于
z
z
z的右子树中但并不是
z
z
z的右孩子。在这种情况下,先用
y
y
y的右孩子替换
y
y
y,然后再用
y
y
y替换
z
z
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)。