14.3 AVL树
AVL树由G.M.Adel‘son-Velskii和Y.M.Landis在1962年发明的自平衡二叉查找树。如果一个二叉树,其左右两子树的高度最多相差1,则称该二叉树是平衡的(balanced)。
对于AVL树中的每一个结点,都有一个平衡因子(balance factor),以表示该结点的左右两分支的高度差,平衡因子有三种状态:
- -1,表示左子树高于右子树;
- 0,表示左右两子树高度相等;
- 1,表示右子树高于左子树。
AVL树的查找和遍历操作,与二叉查找树是类似的。而插入与删除结点就需要作出一些改动,在插入或者删除结点时,保持树的平衡。在保持树的平衡的前提下,该树的高度不会超过1.44 * log n。
14.3.1 插入
对AVL树插入结点,初始过程与二叉查找树一致,但是在插入结点后,如果没有引起任何子树失衡,则无需调整,如:
但是如果引起了子树失衡,就需要重新调整,如:
需要调整的便是二叉查找树中的最深的结点以及离新结点最近的结点。在插入结点之后,平衡因子会在递归回调的过程中重新调整。而遇到的第一个失衡的子树,称为主结点(pivot node)。失衡的AVL树想要恢复平衡,必须要围绕主结点进行旋转(rotation)操作。
将会有四种可能情况:
- 第一种情况:主结点P的平衡因子是-1,即左分支高于右分支,而插入的结点位于左分支。要使该子树重新平衡,主结点P旋转,作为其原先左子结点C的右子结点,而C的右子结点则旋转作为P的左子结点;
- 第二种情况:此种情况涉及到主结点P,其左子结点C,以及C的右子结点G,此种情况下主结点的平衡因子仍然是-1,即左分支高于右分支,而插入结点位于C的右子树中。要想重新恢复平衡,C要向左旋转,作为G的左子结点,而主结点P要向右旋转,作为G的右子结点,即G作为新的主结点,G原来的左子结点,则作为C的右子结点,而G的右子结点则作为P的左子结点;
- 第三种情况,同第一种情况类似,只是主结点的平衡因子是1,即右分支高于左分支,且插入结点位于右分支,所需操作与第一种情况方向相反即可;
- 最后一种情况,与第四种情况类似,只是方向相反;
新平衡因子
当向树中插入新结点时,从根结点到新插入结点路径上的结点的平衡因子将会发生变化,以表示插入操作。显然,平衡因子的变化取决于结点插入前的平衡因子,以及结点插入到哪个分支。
当前平衡因子 | 左分支 | 右分支 |
-1 | -2 | 0 |
0 | -1 | 1 |
1 | 0 | 2 |
在递归回调的过程当中,路径上失衡的结点会对相应的平衡因子进行重新平衡。在旋转操作后,受影响的结点的平衡因子将会发生相应变化。在主结点的子树进行旋转操作后,该子树的高度将减一,则主结点的所有祖先的相应分支都要减一,致使平衡因子重新回归平衡。
G原来状态 | 新P | 新L | 新R | 新G | |
情况1 | – | – | 0 | 0 | – |
情况2 | -1 | 1 | 0 | – | 0 |
0 | 0 | 0 | – | 0 | |
1 | 0 | -1 | – | 0 | |
情况3 | – | 0 | – | 0 | – |
情况4 | -1 | 0 | – | 0 | 1 |
0 | 0 | – | 0 | 0 | |
1 | 0 | – | 0 | -1 |
14.3.2 删除操作 同插入操作类似,删除操作开始还是运行二叉查找树的删除操作,之后再对相应子树进行平衡操作。失衡子树就是从根结点到“”真正删除结点“”的路径过程中结点,注意如果删除的是内结点时,是其逻辑后继直接覆盖在该内结点,然后再删除逻辑后继结点,真正删除的结点应该是逻辑后继结点。 但是跟插入操作不同的是,删除操作是有可能存在失衡传播的问题,即子树重新恢复平衡后,子树高度降低,造成子树的祖先仍处于失衡状态,而插入操作则能使子树重新恢复平衡后,子树的高度得到保留,使得子树的祖先的平衡因子也一并恢复平衡。所以,即便子树恢复平衡,仍然要沿着路径,检查相应结点的平衡因子,若失衡则重新平衡,直至根结点。
删除结点的情况较为复杂,尤其是只操作平衡因子,如果操作高度,则较为简单。 下面是操作平衡因子的方法: 1).按一般的二叉树删点,删除目标结点。二叉树删除特性——最终删除的点至少有一个孩子为空。
2).以移到被删除结点位置的结点P为起点(因为有哨兵叶子,所以P不会为NULL),递归向上回溯
a). 判断P是其父结点的左孩子还是右孩子:如果是左孩子则给父结点平衡因子-1,否则对平衡因子+1,代表删除结点对所在子树平衡性的影响
b). 如果父结点的平衡因子为-1或者1,说明子树的删点没有影响到以父结点为根的子树的高度,可以直接返回。 如果父结点平衡因子为0,说明不需要调整以父结点为根的子树,继续向上回溯。如果父结点平衡因子为-2或者2,则根据不同情况按上面的方法调整,然后继续向上回溯。
#-*-coding: utf-8-*-
# AVL树实现的映射ADT
# 平衡因子常数
LEFT_HIGH = -1
EQUAL_HIGH = 0
RIGHT_HIGH = 1
# 所有非辅助方法与二叉查找树版本类似
class AVLMap(object):
def __init__(self):
self._root = None
self._size = 0
def __len__(self):
return self._size
def __contains__(self, key):
return self._bstSearch(self._root, key) is not None
def add(self, key, value):
node = self._bstSearch(key)
if node is not None:
node.value = value
return False
else:
(self._root, tmp) = self._avlInsert(self._root, key, value)
self._size += 1
return True
def valueOf(self, key):
node = self._bstSearch(self.root, key)
assert node is not None, "Invalid map key."
return node.value
def remove(self, key):
assert key in self, "Invalid map key."
(self._root, tmp) = self._avlRemove(self._root, key)
self._size -= 1
def __iter__(self):
return _BSTMapIterator(self._root)
def _bstSearch(self, subtree, target):
if subtree is None: # 终止条件
return
elif target < subtree.key:
return self._bstSearch(subtree.left, target)
elif target > subtree.key:
return self._bstSearch(subtree.right, target)
else: # 终止条件
return subtree
# 定义左旋和右旋辅助方法,返回重新平衡后子树的根结点
def _avlRotateRight(self, pivot):
C = pivot.left
pivot.left = C.right
C.right = pivot
return C
def _avlRotateLeft(self, pivot):
C = pivot.right
pivot.right = C.left
C.left = pivot
return C
# 将重新恢复平衡的四种情况基于左右两分支的高度,分成两部分
def _avlLeftBalance(self, pivot):
C = pivot.left
if C.bfactor == LEFT_HIGH: # 首先判断C是不是左高,若是左高,则按情况1处理
pivot.bfactor = EQUAL_HIGH # 主结点的平衡因子变为0
C.bfactor = EQUAL_HIGH # 主结点的左子结点的平衡因子也变为0
pivot = _avlRotateRight(pivot) # 仅一次右旋即可
return pivot
else: # 情况2
G = C.right
if G.bfactor == LEFT_HIGH: # 依据主结点左子结点的右子结点的平衡因子判断
pivot.bfactor = RIGHT_HIGH
C.bfactor = EQUAL_HIGH
elif G.bfactor == EQUAL_HIGH:
pivot.bfactor = EQUAL_HIGH
C.bfactor = EQUAL_HIGH
else:
pivot.bfactor = EQUAL_HIGH
C.bfactor = LEFT_HIGH
G.bfactor = EQUAL_HIGH
pivot.left = _avlRotateLeft(C)
pivot = _avlRotateRight(pivot)
return pivot
def _avlRightBalance(self, pivot):
C = pivot.right
if C.bfactor == RIGHT_HIGH: # 情况3
pivot.bfactor = EQUAL_HIGH
C.bfactor = EQUAL_HIGH
pivot = _avlRotateLeft(pivot)
return pivot
else: # 情况4
G = C.left
if G.bfactor == LEFT_HIGH:
pivot.bfactor = RIGHT_HIGH
C.bfactor = EQUAL_HIGH
elif G.bfactor == EQUAL_HIGH:
pivot.bfactor = EQUAL_HIGH
C.bfactor = EQUAL_HIGH
else:
pivot.bfactor = EQUAL_HIGH
C.bfactor = LEFT_HIGH
G.bfactor = EQUAL_HIGH
pivot.right = _avlRotateRight(C)
pivot = _avlRotateLeft(pivot)
return pivot
# 使用递归控制向AVL树插入结点,返回元组,包括根结点的引用和子树是否更高的bool值
def _avlInsert(self, subtree, key, newitem):
if subtree is None: # 空树的情形
subtree = _AVLMapNode(key, newitem)
taller = True
elif key == subtree.key: # ???判断该键是否已存在于树中?(有这个必要吗?_bstSearch()已经可以判断键)
return (subtree, False)
elif key < subtree.key: # 在subtree的左分支插入结点
(subtree, taller) = _avlInsert(subtree.left, key, newitem) # 递归
if taller: # 如果子树高度增长,对平衡因子进行调整
if subtree.bfactor == LEFT_HIGH:
subtree = _avlLeftBalance(subtree) # 在旋转函数中,就已经将相应结点的平衡因子作出了调整
taller = False # 此时增高为False
elif subtree.bfactor == EQUAL_HIGH:
subtree.bfactor = LEFT_HIGH
taller = True
else:
subtree.bfactor = EQUAL_HIGH
taller = False
elif key > subtree.key: # 同上
(subtree, taller) = _avlInsert(subtree.right, key, newitem)
if taller:
if subtree.bfactor == LEFT_HIGH:
subtree.bfactor = EQUAL_HIGH
taller = False
elif subtree.bfactor == EQUAL_HIGH:
subtree.bfactor = RIGHT_HIGH
taller = True
else:
subtree = _avlRightBalance(subtree)
taller = False
return (subtree, taller)
# 在AVL树中删除结点
def _avlRemove(self, subtree, key):
if subtree is None:
return subtree
elif key < subtree.key:
parent = subtree
subtree = _avlRemove(subtree.left, key)
parent.bfactor += 1 # 在左子树中删除结点,根结点的平衡因子加一
if parent.bfactor == 2: # 失衡则进行旋转
parent = self._avlRightBalance(parent)
elif key > subtree.key:
parent = subtree
subtree = _avlRemove(subtree.right, key)
parent.bfactor -= 1 # 在右子树中删除结点,根结点的平衡因子减一
if parent.bfactor -= -2: # 失衡则进行旋转
parent = self._avlLeftBalance(parent)
else:
if subtree.left is None and subtree.right is None:
return subtree
elif subtree.left is None or subtree.right is None: # 删除的结点是只有一个子结点的内结点
if subtree.left is not None:
return subtree.left
else:
return subtree.right
else:
successor = self._bstMinumum(subtree.right) # 搜索目标结点的逻辑后继
subtree.key = successor.key # 逻辑后继替代目标结点
subtree.value = successor.value
subtree.right = self._bstRemove(subtree.right, successor.key) # 删除原逻辑后继
return subtree
class _BSTMapIterator(object):
def __init__(self, root):
self._theStack = Stack()
self._traverseToMinNode(root) # 将二叉搜索树的元素压入栈中,此时不是所有元素已压入栈中
def __iter__(self):
return self
def next(self):
if self._theStack.isEmpty():
raise StopIteration
else:
node = self._theStack.pop()
key = node.key
if node.right is not None: # 将该结点的逻辑后继压入栈中
self._traverseToMinNode(node.right)
# 对二叉搜索树进行遍历,直到找到其最小值为止,期间的元素压入栈中
def _traverseToMinNode(self, subtree):
if subtree is not None:
subtree._theStack.push(subtree)
subtree._traverseToMinNode(subtree.left)
class _AVLMapNode(object):
def __init__(self, key, value):
self.key = key
self.value = value
self.bfactor = EQUAL_HIGH
self.left = None
self.right = None