python數據結構學習筆記-2016-12-10-01-AVL樹

        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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章