Date: 2019-07-13
目录:
11.1 二叉搜索树(11.1.1 遍历二叉搜索树 11.1.2 搜索 11.1.3 插入和删除 11.1.4 python实现 11.1.5 二叉搜索树的性能 )
11.2 平衡搜索树 平衡搜索树的python框架(平衡操作的钩子 旋转和重组的非公开方法 创建树节点工厂)
11.3 AVL树 AVL树的定义 高度平衡属性 (11.3.1 更新操作:插入 删除)AVL树的性能(11.3.2 python实现)
11.4 伸展树 (11.4.1 伸展:zig-zig型 + zig-zag型 + zig型 11.4.2 何时进行伸展 11.4.3 python实现)
11.5 (2,4)树 (11.5.1 多路搜索树:定义+多路搜索树+主要的多路径搜索树数据结构 11.5.2 (2,4)树的操作:插入 删除 ) (2,4)树的性能
11.6 红黑树 (11.6.1 红黑树的操作:插入和删除 )红黑树的性能 11.6.2 python实现
说明:这一章节的内容超级多,其中还有很多是利用图/树进行解释的,所以在此仅仅只能介绍一个大概,和展示一部分的实现代码,想要更加详细的了解这一部分内容的伙伴们,可以看Goodrich的相应书籍哈。
11.1 二叉搜索树
树型数据结构的一个重要用途是用作搜索树,在此我们使用搜索树结构来有效的实现有序映射。映射M的三种基本方法:
M[k]:在映射M中,如果存在与键k相关联的值v,返回v;否则,抛出KeyError。用__getitem__方法来实现。
M[k]=v:在映射M中,将键k与值v相滚关联,如果映射中已经包含键等于k的项,则用v替换现有值。用__setitem__方法来实现。
del M[k]:从映射M里删除键等于k的项;如果M中没有这样的项,则引发KeyError。用__delitem__实现。
有序映射ADT还有许多附加功能,以保证迭代器按照一定的顺序输出键,并且支持额外的搜索,如find_gt(k),find_range(start,stop).
在此处的二叉搜索树是每个节点p存储一个键值对(k,v)的二叉树,使得:
* 存储在p的左子树的键都小于k
* 存储在p的右子树的键都大于k
11.1.1 遍历二叉搜索树
二叉搜索树的中序遍历是按照键增加的顺序进行的
中序遍历可以在线性时间内被执行,当对二叉搜索树进行中序遍历时,根据以上定理我们可以在线性时间内产生一个映射中所有键的有序迭代。支持以下的方法:
first() | 返回一个包含最小键的节点,如果树为空,则返回None |
last() | 返回一个包含最大键的节点,如果树为空,则返回None |
before() | 返回比节点p的键小的所有节点中键最大的节点(中序遍历中在p之前的最后被访问的点),如果p是第一个节点,则None |
after() | 返回比节点p的键大的所有节点中键最小的节点(中序遍历中在p之后的最先被访问的点),如果p是最后一个节点,则None |
二叉搜索树的‘第一个'位置可以从根开始,并且只要左子树存在就继续搜索左子树。左子树搜索完毕之后访问根节点,然后递归搜索右子树。
11.1.2 搜索
二叉搜索树的结构特性产生的最重要的结果是它的同名搜素算法(二叉搜索算法)。将一个二叉搜索树中通过把它表示成决策树的形式定位一个特定的键。于此,每个节点p的问题就是期望键k是否小于、等于或大于存储在节点p的键,这表示为p.key()。如果答案是小于,那么继续搜素左子树;如果答案是等于,那么搜素成功终止;如果答案是大于,则继续搜素右子树,最后得到空子树,就表明没有搜素到。
二叉树搜索的总的运行时间是O(h).
11.1.3 插入和删除
删除一个节点要比插入一个节点更复杂,因为删除的位置可能在树中的任何位置(相比之下,插入总是在搜索路径的最后位置)
11.1.4 python实现
下面是一个基于二叉搜索树的TreeMap类的实现
from ..ch08.linked_binary_tree import LinkedBinaryTree
from ..ch10.map_base import MapBase
class TreeMap(LinkedBinaryTree, MapBase):
"""Sorted map implementation using a binary search tree."""
#---------------------------- override Position class ----------------------------
# 重载前面提出的Position类
class Position(LinkedBinaryTree.Position):
def key(self):
"""Return key of map's key-value pair."""
return self.element()._key
def value(self):
"""Return value of map's key-value pair."""
return self.element()._value
#------------------------------- nonpublic utilities -------------------------------
def _subtree_search(self, p, k): # 1 子树查找k
"""Return Position of p's subtree having key k, or last node searched."""
if k == p.key(): # found match
return p
elif k < p.key(): # search left subtree
if self.left(p) is not None:
return self._subtree_search(self.left(p), k)
else: # search right subtree
if self.right(p) is not None:
return self._subtree_search(self.right(p), k)
return p # unsucessful search
def _subtree_first_position(self, p): # 2 返回p子树的最小键(左)
"""Return Position of first item in subtree rooted at p."""
walk = p
while self.left(walk) is not None: # keep walking left
walk = self.left(walk)
return walk
def _subtree_last_position(self, p): # 3 返回p子树的最大键(右)
"""Return Position of last item in subtree rooted at p."""
walk = p
while self.right(walk) is not None: # keep walking right
walk = self.right(walk)
return walk
#--------------------- public methods providing "positional" support ------------------
def first(self): # 4 返回第一个
"""Return the first Position in the tree (or None if empty)."""
return self._subtree_first_position(self.root()) if len(self) > 0 else None
def last(self): # 5 返回最后一个
"""Return the last Position in the tree (or None if empty)."""
return self._subtree_last_position(self.root()) if len(self) > 0 else None
def before(self, p): # 6 返回p之前的一个键
"""Return the Position just before p in the natural order.
Return None if p is the first position.
"""
self._validate(p) # inherited from LinkedBinaryTree
if self.left(p):
return self._subtree_last_position(self.left(p))
else:
# walk upward
walk = p
above = self.parent(walk)
while above is not None and walk == self.left(above):
walk = above
above = self.parent(walk)
return above
def after(self, p): # 7 返回p之后的一个键
"""Return the Position just after p in the natural order.
Return None if p is the last position.
"""
self._validate(p) # inherited from LinkedBinaryTree
if self.right(p):
return self._subtree_first_position(self.right(p))
else:
walk = p
above = self.parent(walk)
while above is not None and walk == self.right(above):
walk = above
above = self.parent(walk)
return above
def find_position(self, k): # 8
"""Return position with key k, or else neighbor (or None if empty)."""
if self.is_empty():
return None
else:
p = self._subtree_search(self.root(), k)
self._rebalance_access(p) # hook for balanced tree subclasses
return p
#--------------------- public methods for (standard) map interface --------------------
def __getitem__(self, k): # 9
"""Return value associated with key k (raise KeyError if not found)."""
if self.is_empty():
raise KeyError('Key Error: ' + repr(k))
else:
p = self._subtree_search(self.root(), k)
self._rebalance_access(p) # hook for balanced tree subclasses
if k != p.key():
raise KeyError('Key Error: ' + repr(k))
return p.value()
def __setitem__(self, k, v): # 10
"""Assign value v to key k, overwriting existing value if present."""
if self.is_empty():
leaf = self._add_root(self._Item(k,v)) # from LinkedBinaryTree
else:
p = self._subtree_search(self.root(), k)
if p.key() == k:
p.element()._value = v # replace existing item's value
self._rebalance_access(p) # hook for balanced tree subclasses
return
else:
item = self._Item(k,v)
if p.key() < k:
leaf = self._add_right(p, item) # inherited from LinkedBinaryTree
else:
leaf = self._add_left(p, item) # inherited from LinkedBinaryTree
self._rebalance_insert(leaf) # hook for balanced tree subclasses
def delete(self, p): # 11
"""Remove the item at given Position."""
self._validate(p) # inherited from LinkedBinaryTree
if self.left(p) and self.right(p): # p has two children
replacement = self._subtree_last_position(self.left(p))
self._replace(p, replacement.element()) # from LinkedBinaryTree
p = replacement
# now p has at most one child
parent = self.parent(p)
self._delete(p) # inherited from LinkedBinaryTree
self._rebalance_delete(parent) # if root deleted, parent is None
def __delitem__(self, k): # 12
"""Remove item associated with key k (raise KeyError if not found)."""
if not self.is_empty():
p = self._subtree_search(self.root(), k)
if k == p.key():
self.delete(p) # rely on positional version
return # successful deletion complete
self._rebalance_access(p) # hook for balanced tree subclasses
raise KeyError('Key Error: ' + repr(k))
def __iter__(self): # 13
"""Generate an iteration of all keys in the map in order."""
p = self.first()
while p is not None:
yield p.key()
p = self.after(p)
#--------------------- public methods for sorted map interface ---------------------
def __reversed__(self): # 14
"""Generate an iteration of all keys in the map in reverse order."""
p = self.last()
while p is not None:
yield p.key()
p = self.before(p)
def find_min(self): # 15
"""Return (key,value) pair with minimum key (or None if empty)."""
if self.is_empty():
return None
else:
p = self.first()
return (p.key(), p.value())
def find_max(self): # 16
"""Return (key,value) pair with maximum key (or None if empty)."""
if self.is_empty():
return None
else:
p = self.last()
return (p.key(), p.value())
def find_le(self, k): # 17
"""Return (key,value) pair with greatest key less than or equal to k.
Return None if there does not exist such a key.
"""
if self.is_empty():
return None
else:
p = self.find_position(k)
if k < p.key():
p = self.before(p)
return (p.key(), p.value()) if p is not None else None
def find_lt(self, k): # 18
"""Return (key,value) pair with greatest key strictly less than k.
Return None if there does not exist such a key.
"""
if self.is_empty():
return None
else:
p = self.find_position(k)
if not p.key() < k:
p = self.before(p)
return (p.key(), p.value()) if p is not None else None
def find_ge(self, k): # 19
"""Return (key,value) pair with least key greater than or equal to k.
Return None if there does not exist such a key.
"""
if self.is_empty():
return None
else:
p = self.find_position(k) # may not find exact match
if p.key() < k: # p's key is too small
p = self.after(p)
return (p.key(), p.value()) if p is not None else None
def find_gt(self, k): # 20
"""Return (key,value) pair with least key strictly greater than k.
Return None if there does not exist such a key.
"""
if self.is_empty():
return None
else:
p = self.find_position(k)
if not k < p.key():
p = self.after(p)
return (p.key(), p.value()) if p is not None else None
def find_range(self, start, stop): # 21
"""Iterate all (key,value) pairs such that start <= key < stop.
If start is None, iteration begins with minimum key of map.
If stop is None, iteration continues through the maximum key of map.
"""
if not self.is_empty():
if start is None:
p = self.first()
else:
# we initialize p with logic similar to find_ge
p = self.find_position(start)
if p.key() < start:
p = self.after(p)
while p is not None and (stop is None or p.key() < stop):
yield (p.key(), p.value())
p = self.after(p)
#--------------------- hooks used by subclasses to balance a tree ---------------------
def _rebalance_insert(self, p): # 22
"""Call to indicate that position p is newly added."""
pass
def _rebalance_delete(self, p): # 23
"""Call to indicate that a child of p has been removed."""
pass
def _rebalance_access(self, p): # 24
"""Call to indicate that position p was recently accessed."""
pass
#--------------------- nonpublic methods to support tree balancing -----------------
# 下面是,为平衡搜素树的子类提供非公开的实用程序(_relink,_rotate,_restructure函数)
def _relink(self, parent, child, make_left_child): # 25
"""Relink parent node with child node (we allow child to be None)."""
if make_left_child: # make it a left child
parent._left = child
else: # make it a right child
parent._right = child
if child is not None: # make child point to parent
child._parent = parent
def _rotate(self, p): # 26
"""Rotate Position p above its parent.
Switches between these configurations, depending on whether p==a or p==b.
b a
/ \ / \
a t2 t0 b
/ \ / \
t0 t1 t1 t2
Caller should ensure that p is not the root.
"""
"""Rotate Position p above its parent."""
x = p._node
y = x._parent # we assume this exists
z = y._parent # grandparent (possibly None)
if z is None:
self._root = x # x becomes root
x._parent = None
else:
self._relink(z, x, y == z._left) # x becomes a direct child of z
# now rotate x and y, including transfer of middle subtree
if x == y._left:
self._relink(y, x._right, True) # x._right becomes left child of y
self._relink(x, y, False) # y becomes right child of x
else:
self._relink(y, x._left, False) # x._left becomes right child of y
self._relink(x, y, True) # y becomes left child of x
def _restructure(self, x): # 27
"""Perform a trinode restructure among Position x, its parent, and its grandparent.
Return the Position that becomes root of the restructured subtree.
Assumes the nodes are in one of the following configurations:
z=a z=c z=a z=c
/ \ / \ / \ / \
t0 y=b y=b t3 t0 y=c y=a t3
/ \ / \ / \ / \
t1 x=c x=a t2 x=b t3 t0 x=b
/ \ / \ / \ / \
t2 t3 t0 t1 t1 t2 t1 t2
The subtree will be restructured so that the node with key b becomes its root.
b
/ \
a c
/ \ / \
t0 t1 t2 t3
Caller should ensure that x has a grandparent.
"""
"""Perform trinode restructure of Position x with parent/grandparent."""
y = self.parent(x)
z = self.parent(y)
if (x == self.right(y)) == (y == self.right(z)): # matching alignments
self._rotate(y) # single rotation (of y)
return y # y is new subtree root
else: # opposite alignments
self._rotate(x) # double rotation (of x)
self._rotate(x)
return x # x is new subtree root
11.1.5 二叉搜索树的性能
几乎所有的操作都有一个最坏的运行时间,他取决于树的高度h,这是因为,大多数操作都依赖于沿树的特定路径中每个节点的恒定工作量,且最大路径长度与树的高度成正比。
操作 | 运行时间 |
k in T | O(h) |
T[k], T[k]=v | O(h) |
T.delete(p),del T[k] | O(h) |
T.find_position(k) | O(h) |
T.first(),T.last(),T.find_min(),T.find_max() | O(h) |
T.before(p),T.after(p) | O(h) |
T.find_lt(k),T.find_le(k),T.find_gt(k),T.find_ge(k) | O(h) |
T.find_range(start,,stop) | O(s+h) |
iter(T),reverse(T) | O(n) |
从上面的性能分析结果来看,只有在树的高度比较小的情况下,二叉搜索树T才是实现n个实体的映射的高效算法。通常来说,通过一系列随机的插入或删除键操作生成的n个键的二叉搜索树的期望复杂度是O(logn)。
11.2 平衡搜索树
平衡指的是:对于树中每一个节点p,其相应的左右两边的子树高度相差不超过1。
在上面结尾处,注意到:如果假设有一系列的插入和删除操作,标准二叉搜索树的基本映射操作的运行时间是O(logn).但是由于某些操作系列可能会生成高度与n成比例的不平衡树,这种树的时间复杂度是O(n)。因此在接下来的四部分中,探讨了4中能提供更强壮能保证的搜索树算法平衡二叉搜素树、AVL树、伸展树、红黑树),平衡二叉搜索树的主要操作是旋转,而后面三种是基于少量操作对标准二叉搜索树进行扩展去重新调整树并降低树的高度。
平衡搜索树的python框架已经在上面的上面的代码中进行了展示:_relink、_rotate、_restructure这三个函数便是相应的框架。
11.3 AVL 树
高度平衡性:对于T中每一个位置p,p的孩子的高度相差最多为1.
任何满足高度平衡属性的二叉搜索树被称为AVL树。高度平衡属性所带来的一个直接结果是AVL树子树本身就是一棵AVL树。
命题:一棵存有n个节点的AVL树的高度是O(logn)。
11.3.1 更新操作(插入和删除)
插入:在树的叶子节点处插入一个节点后,如果仍然满足AVL树的定义,无需管;如果违反了AVL树的定义,则找到相应的不符合的节点,然后将其子树进行判断并移动和旋转重组。
删除:同理,删除一个叶子节点之后,若不再满足高度平衡属性,则进行相应的重组。
AVL树的性能分析:
操作 | 运行时间 |
k in T | O(logn) |
T[k], T[k]=v | O(logn) |
T.delete(p),del T[k] | O(logn) |
T.find_position(k) | O(logn) |
T.first(),T.last(),T.find_min(),T.find_max() | O(logn) |
T.before(p),T.after(p) | O(logn) |
T.find_lt(k),T.find_le(k),T.find_gt(k),T.find_ge(k) | O(logn) |
T.find_range(start,,stop) | O(s + logn) |
iter(T),reverse(T) | O(logn) |
11.3.2 python 实现
from .binary_search_tree import TreeMap
class AVLTreeMap(TreeMap):
"""Sorted map implementation using an AVL tree."""
#-------------------------- nested _Node class --------------------------
class _Node(TreeMap._Node):
"""Node class for AVL maintains height value for balancing.
We use convention that a "None" child has height 0, thus a leaf has height 1.
"""
__slots__ = '_height' # additional data member to store height
def __init__(self, element, parent=None, left=None, right=None):
super().__init__(element, parent, left, right)
self._height = 0 # will be recomputed during balancing
def left_height(self): # 计算左子树的高度
return self._left._height if self._left is not None else 0
def right_height(self): # 计算右子树的高度
return self._right._height if self._right is not None else 0
#------------------------- positional-based utility methods -------------------------
def _recompute_height(self, p): # 计算节点高度
p._node._height = 1 + max(p._node.left_height(), p._node.right_height())
def _isbalanced(self, p): # 判断是否仍是满足高度平衡属性
return abs(p._node.left_height() - p._node.right_height()) <= 1
def _tall_child(self, p, favorleft=False): # parameter controls tiebreaker
if p._node.left_height() + (1 if favorleft else 0) > p._node.right_height():
return self.left(p)
else:
return self.right(p)
def _tall_grandchild(self, p):
child = self._tall_child(p)
# if child is on left, favor left grandchild; else favor right grandchild
alignment = (child == self.left(p))
return self._tall_child(child, alignment)
def _rebalance(self, p):
while p is not None:
old_height = p._node._height # trivially 0 if new node
if not self._isbalanced(p): # imbalance detected!
# perform trinode restructuring, setting p to resulting root,
# and recompute new local heights after the restructuring
p = self._restructure(self._tall_grandchild(p))
self._recompute_height(self.left(p))
self._recompute_height(self.right(p))
self._recompute_height(p) # adjust for recent changes
if p._node._height == old_height: # has height changed?
p = None # no further changes needed
else:
p = self.parent(p) # repeat with parent
#---------------------------- override balancing hooks ----------------------------
def _rebalance_insert(self, p):
self._rebalance(p)
def _rebalance_delete(self, p):
self._rebalance(p)
11.4 伸展树
这种结构从概念上完全不同于本章中的其他平衡搜索树,因为伸展树在树的高度上没有一个严格的数的对数上界。事实上,伸展树无需依赖额外的高度,平衡或与此树节点关联的其他辅助数据。伸展树的效率取决于某一位置移动到根的操作(称为伸展),每次在插入、删除或者甚至搜索都要从最底层的位置p开始
11.4.1 伸展
我们通过一系列的重组将x移动到T的根来对x进行扩展。进行特定的重组是很重要的,因为将节点x移动到根节点T仅仅通过一些序列的重组是不够的。我们将x向上移动执行特定的操作取决于x、其父节点y和x的祖父节点z的相对位置。先考虑如下三种情况:
* zig-zig型:节点x和父节点y都在树的左边或者都在树的右边:移动节点x,使y节点为x节点的一个孩子,z节点为y节点的一个孩子。(x为祖父节点)
* zig-zag型:节点x和节点y一个是左孩子,一个是右孩子:移动节点x,使其拥有孩子节点y和z.(x为y和z的父节点)
* zig型:x没有祖父节点:将x提升到y之上,使得节点y为节点x的孩子节点。(x为y的父节点)
上面三种最后都使得x节点最后变为伸展树的根节点。
11.4.2 何时进行伸展
** 当搜索键k时,如果在位置p处找到k,则伸展p;否则,在搜索失败的位置伸展叶子节点
**当插入键k时,我们将伸展新插入的内部节点k
** 当删除键k,在位置p进行伸展,其中p是被移除节点的父节点。
11.4.3 Python实现(伸展树的实现)
from .binary_search_tree import TreeMap
class SplayTreeMap(TreeMap):
"""Sorted map implementation using a splay tree."""
#--------------------------------- splay operation --------------------------------
def _splay(self, p):
while p != self.root():
parent = self.parent(p)
grand = self.parent(parent)
if grand is None:
# zig case
self._rotate(p)
elif (parent == self.left(grand)) == (p == self.left(parent)):
# zig-zig case
self._rotate(parent) # move PARENT up
self._rotate(p) # then move p up
else:
# zig-zag case
self._rotate(p) # move p up
self._rotate(p) # move p up again
#---------------------------- override balancing hooks ----------------------------
def _rebalance_insert(self, p):
self._splay(p)
def _rebalance_delete(self, p):
if p is not None:
self._splay(p)
def _rebalance_access(self, p):
self._splay(p)
11.4.4 伸展树的性能分析(没看懂,略)
11.5 (2,4)树
(2,4)树是多路搜索树这种通用数据结构的一个特殊的例子。
多路搜素树:令w为有序树的一个节点。如果w有d个孩子,则称w是d_node.我们将一棵多路搜素树定义为有以下属性的有序树T:
* T的每个内部节点至少有两个孩子。也就是说每个内部节点是一个d_node,其中d>=2.
* T的每个内部节点d_node w,其孩子c_1,c_2,……,c_d按顺序存储d-1个键-值对(k_1,v_1),(k_2,v_2),……,(k_d,v_d)
* 通常定义 和
.每个条目(k,v)存储在一个以
为根的w的子树一个节点上,其中
命题:一棵有n个节点的多路搜素树有n+1外部节点。
(2,4)树的操作:插入和删除
略
(2,4)树的性能:
在有序映射ADT方面,(2,4)树的渐近性能和AVL树是一样的,并且对于大多数操作都是具有对数边界的。有n个键值对的(2,4)树的时间复杂度的分析基于以下几点:
* 存储n个节点的(2,4)树的高度是O(logn)
* 分裂、交换或合并操作需要O(1)时间
* 搜素、插入或删除一个节点需要访问O(logn)个节点
11.6 红黑树
虽然AVL树和(2,4)树都具有很多很好的特性,但他们也有一些缺点。例如:AVL树删除后可能需要要执行的多重组操作(旋转);(2,4)树在插入和删除之后可能需要进行许多分裂或融合操作。
在本部分介绍的红黑树没有操作之后的后续补充操作的缺点。
从形式上来讲,红黑树是一棵带有红色和黑色节点的二叉搜索树,具有下面的属性:
* 根属性:根节点是黑色的
* 红色属性:红色节点的子节点是黑色的,也就是说,从每个叶子到根的所有路径上不能出现连续的两个红色节点。
* 深度属性:具有零个或一个子节点的所有节点都具有相同的黑色深度(黑色祖先节点的数量)
命题:有n个条目的红黑树的高度是O(logn).
11.6.1 红黑树的操作:插入和删除
插入之后可能会出现’双红色‘问题,分为两种情况讨论:具体太多了,略,详细见书。
删除之后也需要讨论删除后的三种情况:
红黑树的性能:
根据有序映射ADYT,红黑树的渐进性能与AVL树或(2,4)树的渐近性能相同,对于大多数操作保证了对数时间界限.红黑树的主要优点在于插入或删除只需要常数步的调整操作(这相当于AVL树或(2,4)树,在最坏的情况下,两者的每个映射结构的调整均需要对数倍的时间操作)。也就是说,在红黑树的插入或删除操作中,搜索一次需要对数级时间,并且可能需要对数倍的级联向上重新着色操作。对于单个的映射操作有一个常数数量的旋转或调整操作。
命题:在一棵存储n个项目的红黑树中插入一个项可在O(logn)的时间内完成,并且需要O(logn)的重新着色且至多需要一次的trinode重组
命题:在一棵存储n个项目的红黑树中删除一个项可在O(logn)的时间内完成,并且需要O(logn)的重新着色且最多需要两次调整操作。
11.6.2 python实现
from .binary_search_tree import TreeMap
class RedBlackTreeMap(TreeMap):
"""Sorted map implementation using a red-black tree."""
#-------------------------- nested _Node class --------------------------
class _Node(TreeMap._Node):
"""Node class for red-black tree maintains bit that denotes color."""
__slots__ = '_red' # add additional data member to the Node class
def __init__(self, element, parent=None, left=None, right=None):
super().__init__(element, parent, left, right)
self._red = True # new node red by default
#------------------------- positional-based utility methods -------------------------
# we consider a nonexistent child to be trivially black
def _set_red(self, p): p._node._red = True
def _set_black(self, p): p._node._red = False
def _set_color(self, p, make_red): p._node._red = make_red
def _is_red(self, p): return p is not None and p._node._red
def _is_red_leaf(self, p): return self._is_red(p) and self.is_leaf(p)
def _get_red_child(self, p):
"""Return a red child of p (or None if no such child)."""
for child in (self.left(p), self.right(p)):
if self._is_red(child):
return child
return None
#------------------------- support for insertions -------------------------
def _rebalance_insert(self, p):
self._resolve_red(p) # new node is always red
def _resolve_red(self, p):
if self.is_root(p):
self._set_black(p) # make root black
else:
parent = self.parent(p)
if self._is_red(parent): # double red problem
uncle = self.sibling(parent)
if not self._is_red(uncle): # Case 1: misshapen 4-node
middle = self._restructure(p) # do trinode restructuring
self._set_black(middle) # and then fix colors
self._set_red(self.left(middle))
self._set_red(self.right(middle))
else: # Case 2: overfull 5-node
grand = self.parent(parent)
self._set_red(grand) # grandparent becomes red
self._set_black(self.left(grand)) # its children become black
self._set_black(self.right(grand))
self._resolve_red(grand) # recur at red grandparent
#------------------------- support for deletions -------------------------
def _rebalance_delete(self, p):
if len(self) == 1:
self._set_black(self.root()) # special case: ensure that root is black
elif p is not None:
n = self.num_children(p)
if n == 1: # deficit exists unless child is a red leaf
c = next(self.children(p))
if not self._is_red_leaf(c):
self._fix_deficit(p, c)
elif n == 2: # removed black node with red child
if self._is_red_leaf(self.left(p)):
self._set_black(self.left(p))
else:
self._set_black(self.right(p))
def _fix_deficit(self, z, y):
"""Resolve black deficit at z, where y is the root of z's heavier subtree."""
if not self._is_red(y): # y is black; will apply Case 1 or 2
x = self._get_red_child(y)
if x is not None: # Case 1: y is black and has red child x; do "transfer"
old_color = self._is_red(z)
middle = self._restructure(x)
self._set_color(middle, old_color) # middle gets old color of z
self._set_black(self.left(middle)) # children become black
self._set_black(self.right(middle))
else: # Case 2: y is black, but no red children; recolor as "fusion"
self._set_red(y)
if self._is_red(z):
self._set_black(z) # this resolves the problem
elif not self.is_root(z):
self._fix_deficit(self.parent(z), self.sibling(z)) # recur upward
else: # Case 3: y is red; rotate misaligned 3-node and repeat
self._rotate(y)
self._set_black(y)
self._set_red(z)
if z == self.right(y):
self._fix_deficit(z, self.left(z))
else:
self._fix_deficit(z, self.right(z))
通过重写嵌套_Node类的定义引入一个附加的布尔值来表示节点的当前颜色。我们的构造函数有意将新节点的颜色设置为红色,以符合插入节点的方法。另外添加了几个附加的实用功能,帮助设置节点的颜色和查询各种条件。
当一个元素已作为一个叶子节点被插入树中,_rebalance_insert的钩子将被调用,使我们有机会修改树的结构。新节点默认情况下是红色的,所以我们只需要寻找新节点的情况,新节点是根(在这种情况下,新节点应该是黑色的),或者有一个双重红色问题,因为新节点的父节点可能是红色的。
删除之后的再平衡也遵循11.6.1节所描述的情况分析。一个额外的挑战是,在_rebalance_delete被调用时,旧节点已经从树中移除。在移除节点的父节点上调用钩子。某些情况下分析取决于知道关于被去除节点的属性。幸运的是,我们可以根据红黑树的信息将进行逆向处理,。特别的是,如果p表示移除节点的父节点,他必须是:
* 如果p没有子节点,移除的节点是红叶子节点
*如果p有一个子节点,已删除节点是一个黑叶,造成空缺,除非剩下的一个子节点是一个红色的叶子
* 如果p有两个子节点,则移除的是一个被升级并且有红色子节点的黑色节点