14.3 AVL樹
AVL樹由G.M.Adel‘son-Velskii和Y.M.Landis在1962年發明的自平衡二叉查找樹。如果一個二叉樹,其左右兩子樹的高度最多相差1,則稱該二叉樹是平衡的(balanced)。
對於AVL樹中的每一個結點,都有一個平衡因子(balance factor),以表示該結點的左右兩分支的高度差,平衡因子有三種狀態:
- -1,表示左子樹高於右子樹;
- 0,表示左右兩子樹高度相等;
- 1,表示右子樹高於左子樹。
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 |
#-*-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