1、定義
二叉樹的建構過程中,若元素的插入先後順序不同,則會影響二叉樹最終的結構,是以引入在key插入時一直保持平衡的二叉查找樹: AVL樹。AVL是發明者的名字縮寫: G.M. AdelsonVelskii and E.M. Landis。利用AVL樹實作ADT Map, 基本上與BST的實作相同,不同之處僅在于二叉樹的生成與維護過程
- AVL樹的實作中, 需要對每個節點跟蹤“平衡因子balance factor”參數
-
平衡因子是根據節點的左右子樹的高度來定義的, 确切地說, 是左右子樹高度差: balanceFactor =
height(leftSubTree) -height(rightSubTree)
- 如果平衡因子大于0,稱為“左重left-heavy”,小于零稱為“右重right-heavy”,平衡因子等于0,則稱作平衡。
如果一個二叉查找樹中每個節點的平衡因子都在-1, 0, 1之間, 則把這個二叉搜尋樹稱為平衡樹
2、功能分析
在平衡樹操作過程中, 有節點的平衡因子超出此範圍, 則需要一個重新平衡的過程,要保持BST的性質!
既然AVL平衡樹确實能夠改進BST樹的性能, 避免退化情形,我們來看看向AVL樹插入一個新key, 如
何才能保持AVL樹的平衡性質,首先, 作為BST, 新key必定以葉節點形式插入到AVL樹中
- ❖葉節點的平衡因子是0, 其本身無需重新平衡
- ❖但會影響其父節點的平衡因子: 作為左子節點插入,則父節點平衡因子會增加1; 作為右子節點插入,則父節點平衡因子會減少1。
這種影響可能随着其父節點到根節點的路徑一直傳遞上去, 直到:
- 傳遞到根節點為止;
- 或者某個父節點平衡因子被調整到0,不再影響上層節點的平衡因子為止。 重新定義_put方法即可
def _put(self,key,val,currentNode):
if key < currentNode.key:
if currentNode.hasLeftChild():
self._put(key,val,currentNode.leftChild)
else:
currentNode.leftChild = TreeNode(key,val,parent=currentNode)
self.updateBalance(currentNode.leftChild)
else:
if currentNode.hasRightChild():
self._put(key,val,currentNode.rightChild)
else:
currentNode.rightChild = TreeNode(key,val,parent=currentNode)
self.updateBalance(currentNode.rightChild)
UpdateBalance方法
def updateBalance(self,node):
if node.balanceFactor > 1 or node.balanceFactor < -1:
self.rebalance(node)
return
if node.parent != None:
if node.isLeftChild():
node.parent.balanceFactor += 1
elif node.isRightChild():
node.parent.balanceFactor -= 1
if node.parent.balanceFactor != 0:
self.updateBalance(node.parent)
rebalance重新平衡
主要手段 :将不平衡的子樹進行旋轉rotation
視**“左重”或者“右重”進行不同方向的旋轉同時更新相關父節點引用,更新旋轉後被影響節點的平衡因子**
下圖是一個**“右重”子樹**A的左旋轉(并保持BST性質),将右子節點B提升為子樹的根,将舊根節點A作為新根節點B的左子節點,如果新根節點B原來有左子節點,則将此節點設定為A的右子節點(A的右子節點一定有空)
更複雜一些的情況:如圖的“左重”子樹右旋轉
旋轉後,新根節點将舊根節點作為右子節點,但是新根節點原來已有右子節點,需要将原有的右子節點重新定位!
原有的右子節點D改到舊根節點E的左子節點
同樣, E的左子節點在旋轉後一定有空
def rebalance(self,node):
if node.balanceFactor < 0:
if node.rightChild.balanceFactor > 0:
# Do an LR Rotation
self.rotateRight(node.rightChild)
self.rotateLeft(node)
else:
# single left
self.rotateLeft(node)
elif node.balanceFactor > 0:
if node.leftChild.balanceFactor < 0:
# Do an RL Rotation
self.rotateLeft(node.leftChild)
self.rotateRight(node)
else:
# single right
self.rotateRight(node)
def rotateLeft(self,rotRoot):
newRoot = rotRoot.rightChild
rotRoot.rightChild = newRoot.leftChild
if newRoot.leftChild != None:
newRoot.leftChild.parent = rotRoot
newRoot.parent = rotRoot.parent
if rotRoot.isRoot():
self.root = newRoot
else:
if rotRoot.isLeftChild():
rotRoot.parent.leftChild = newRoot
else:
rotRoot.parent.rightChild = newRoot
newRoot.leftChild = rotRoot
rotRoot.parent = newRoot
rotRoot.balanceFactor = rotRoot.balanceFactor + 1 - min(newRoot.balanceFactor, 0)
newRoot.balanceFactor = newRoot.balanceFactor + 1 + max(rotRoot.balanceFactor, 0)
def rotateRight(self,rotRoot):
newRoot = rotRoot.leftChild
rotRoot.leftChild = newRoot.rightChild
if newRoot.rightChild != None:
newRoot.rightChild.parent = rotRoot
newRoot.parent = rotRoot.parent
if rotRoot.isRoot():
self.root = newRoot
else:
if rotRoot.isRightChild():
rotRoot.parent.rightChild = newRoot
else:
rotRoot.parent.leftChild = newRoot
newRoot.rightChild = rotRoot
rotRoot.parent = newRoot
rotRoot.balanceFactor = rotRoot.balanceFactor - 1 - max(newRoot.balanceFactor, 0)
newRoot.balanceFactor = newRoot.balanceFactor - 1 + min(rotRoot.balanceFactor, 0)
3、代碼實作
class AVLTree(BinarySearchTree):
def _put(self,key,val,currentNode):
if key < currentNode.key:
if currentNode.hasLeftChild():
self._put(key,val,currentNode.leftChild)
else:
currentNode.leftChild = TreeNode(key,val,parent=currentNode)
self.updateBalance(currentNode.leftChild)
else:
if currentNode.hasRightChild():
self._put(key,val,currentNode.rightChild)
else:
currentNode.rightChild = TreeNode(key,val,parent=currentNode)
self.updateBalance(currentNode.rightChild)
def updateBalance(self,node):
if node.balanceFactor > 1 or node.balanceFactor < -1:
self.rebalance(node)
return
if node.parent != None:
if node.isLeftChild():
node.parent.balanceFactor += 1
elif node.isRightChild():
node.parent.balanceFactor -= 1
if node.parent.balanceFactor != 0:
self.updateBalance(node.parent)
def rebalance(self,node):
if node.balanceFactor < 0:
if node.rightChild.balanceFactor > 0:
# Do an LR Rotation
self.rotateRight(node.rightChild)
self.rotateLeft(node)
else:
# single left
self.rotateLeft(node)
elif node.balanceFactor > 0:
if node.leftChild.balanceFactor < 0:
# Do an RL Rotation
self.rotateLeft(node.leftChild)
self.rotateRight(node)
else:
# single right
self.rotateRight(node)
def rotateLeft(self,rotRoot):
newRoot = rotRoot.rightChild
rotRoot.rightChild = newRoot.leftChild
if newRoot.leftChild != None:
newRoot.leftChild.parent = rotRoot
newRoot.parent = rotRoot.parent
if rotRoot.isRoot():
self.root = newRoot
else:
if rotRoot.isLeftChild():
rotRoot.parent.leftChild = newRoot
else:
rotRoot.parent.rightChild = newRoot
newRoot.leftChild = rotRoot
rotRoot.parent = newRoot
rotRoot.balanceFactor = rotRoot.balanceFactor + 1 - min(newRoot.balanceFactor, 0)
newRoot.balanceFactor = newRoot.balanceFactor + 1 + max(rotRoot.balanceFactor, 0)
def rotateRight(self,rotRoot):
newRoot = rotRoot.leftChild
rotRoot.leftChild = newRoot.rightChild
if newRoot.rightChild != None:
newRoot.rightChild.parent = rotRoot
newRoot.parent = rotRoot.parent
if rotRoot.isRoot():
self.root = newRoot
else:
if rotRoot.isRightChild():
rotRoot.parent.rightChild = newRoot
else:
rotRoot.parent.leftChild = newRoot
newRoot.rightChild = rotRoot
rotRoot.parent = newRoot
rotRoot.balanceFactor = rotRoot.balanceFactor - 1 - max(newRoot.balanceFactor, 0)
newRoot.balanceFactor = newRoot.balanceFactor - 1 + min(rotRoot.balanceFactor, 0)
4、算法分析
AVL樹最差情形下的性能:即平衡因子為1或者-1,下圖列出平衡因子為1的“左重”AVL樹,樹的高度從1開始,問題規模(總節點數N)和比對次數(樹的高度h)之間的關系為:最多搜尋次數h和規模N的關系, 可以說AVL樹的搜尋時間複雜度為O(log n)