before(p) :返回比节点 p 的键小的所有节点中键最大的节点(即中序遍历中在 p 之前的节点),如果p是第一个节点,则返回 None 。
after(p) :返回比节点 p 的键大的所有节点中键最小的节点(即中序遍历中在 p 之后的节点),如果p是最后一个节点,则返回 None 。
以 after() 为例,展示如何返回在中序遍历时 p 后面的节点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Algorithm after(p): if right(p) isnotNone# p 有右子节点 walk = right(p) while left(walk) isnotNone do walk = left(walk) # 右子树的最左子节点为 after(p) return walk
else walk = p ancestor = parent(walk) while ancestor isnotNoneand walk == right(ancestor) do walk = ancestor ancestor = parent(walk) # 第一个作为祖先节点的左子节点的父节点为 after(p) return ancestor
显然,利用这种方法最坏情况便是遍历树的每一层,所以复杂度为 O(h) 其中 h 为树的层数。
1.2 搜索
搜索键 k 时,我们从树根开始向下搜索,在每个节点 p 上,下一步操作由当前节点的键与 k 的比较结果而决定:
若 p.key() > k ,则继续搜索左子树
若 p.key() == k ,则搜索成功且终止
若 p.key() < k ,则继续搜索右子树
若最后探查到空子树,则搜索失败
实现可以采用递归的方式,伪代码如下:
1 2 3 4 5 6 7 8 9 10 11
Algorithm TreeSearch(T, p, k): if k == p.key() then return p # 搜索成功 elseif k < p.key() and T.left(p) isnotNone then return TreeSearch(T, T.left(p), k) # 递归搜索左子树
elseif k > p.key() and T.right(p) isnotNone then return TreeSearch(T, T.right(p), k) # 递归搜索右子树
return p
类似地,这里的搜索查看也是最坏需要遍历树的每一层,复杂度为 O(h) 。
1.3 插入和删除
1.3.1 插入
对于插入操作,首先搜索键 k 。如找到则重新对其值进行赋值,否则返回搜索失败时最后一个探查的节点 p :
若 k < p.key() ,则将包含 k的新节点作为 p 的左孩子
若 k > p.key() ,则将包含 k 的新节点作为 p 的右孩子
伪代码见下:
1 2 3 4 5 6 7 8 9 10
Algorithm TreeInsert(T, k, v): p = TreeSearch(T, T.root(), k) # 先搜索键 k if k == p.key() then Set p’s value to v # 找到则修改值 # 否则,查看当前 key 与需要插入的 (k, v) 比较 elseif k < p.key() then add node with item (k, v) as left child of p else add node with item (k, v) as right child of p
1.3.2 删除
删除操作比插入更复杂,因为删除的可能是度为 2 的节点。首先搜索键 k 。如找到,设找到的节点为 p,则:
若 p 无孩子节点,直接删除即可
若 p 有一个孩子节点 r,则删除 p 且用其孩子节点替代它
但是,若 p 有两个孩子节点,则:
使用 before(p) 找到中序遍历中节点 p 的前一个节点 r
用节点 r 替代节点 p
删除节点 r
删除 before(p) 的方法一定能保持树的结构完整,前一个节点 r 一定只有 1 个或 0 个孩子节点。
try: from .linked_binary_tree import LinkedBinaryTree from .map_base import MapBase except ImportError: from linked_binary_tree import LinkedBinaryTree from map_base import MapBase
# --------------- 非公有方法 --------------- def_subtree_search(self, p, k): """搜索:从 p 节点开始搜索键为 k 的子节点,或最后一个搜索到的节点""" if k == p.key(): return p elif k < p.key(): ifself.left(p) isnotNone: returnself._subtree_search(self.left(p), k) else: ifself.right(p) isnotNone: returnself._subtree_search(self.right(p), k) return p # 未搜索到则返回自己
def_subtree_first_position(self, p): """从 p 节点开始搜索,一直搜索到最左的子节点""" walk = p whileself.left(walk) isnotNone: walk = self.left(walk) return walk
def_subtree_last_position(self, p): """从 p 节点开始搜索,一直搜索到最右的子节点""" walk = p whileself.right(walk) isnotNone: walk = self.right(walk) return walk
defbefore(self, p): """中序遍历 p 节点的前一个元素""" self._validate(p) # 判断是否为当前树的节点,不重要 ifself.left(p): returnself._subtree_last_position(self.left(p)) else: walk = p above = self.parent(walk) while above isnotNoneand walk == self.left(above): walk = above above = self.parent(walk) return above
defafter(self, p): """中序遍历 p 节点的后一个元素""" self._validate(p) ifself.right(p): returnself._subtree_first_position(self.right(p)) else: walk = p above = self.parent(walk) while above isnotNoneand walk == self.right(above): walk = above above = self.parent(walk) return above
deffind_position(self, k): """找到键为 k 的节点""" ifself.is_empty(): returnNone else: p = self._subtree_search(self.root(), k) self._rebalance_access(p) # 平衡树结构的钩子方法,实现方法见后 return p
deffind_ge(self, k): """大于或等于 k 的最小的键 (key, value)""" ifself.is_empty(): returnNone else: p = self.find_position(k) if p.key() < k: p = self.after(p) return (p.key(), p.value()) if p isnotNoneelseNone
deffind_range(self, start, stop): """迭代器:(key, value) 使得 start <= key < stop""" ifnotself.is_empty(): if start isNone: p = self.first() else: p = self.find_position(start) if p.key() < start: p = self.after(p)
while p isnotNoneand (stop isNoneor p.key() < stop): yield (p.key(), p.value()) p = self.after(p)
def__delitem__(self, k): """删除键为 k 元素 del M[k]""" ifnotself.is_empty(): p = self._subtree_search(self.root(), k) if k == p.key(): self.delete(p) return self._rebalance_access(p) raise KeyError('Key Error: ' + repr(k))
def_rotate(self, p): """将子节点 p 旋转上去,p 的父节点选择下来,祖先节点不变""" x = p._node y = x._parent z = y._parent if z isNone: self._root = x x._parent = None else: # x 成为 z 的子节点 self._relink(z, x, make_left_child=(y == z._left)) if x == y._left: # x 右子树成为 y 左子节点 self._relink(y, x._right, make_left_child=True) # y 成为 x 的右子节点 self._relink(x, y, make_left_child=False) else: # x 左子树成为 y 的右子节点 self._relink(y, x._left, make_left_child=False) # y 成为 x 的左子节点 self._relink(x, y, make_left_child=True)
def_restructure(self, x): """trinode 重构:将节点 x 与它的父节点和祖先节点重构""" y = self.parent(x) z = self.parent(y) if (x == self.right(y)) == (y == self.right(z)): self._rotate(y) # 一次旋转 (zig-zig) return y else: # 两次旋转 (zig-zag) self._rotate(x) self._rotate(x) return x
3 AVL 树
在传统的二叉搜索树上施加限制,得到 AVL (Adelson-Velskii and Landis)树,从而保证所有基本映射操作最坏都在对数复杂度下完成。