[DS] AVL 트리 (AVL Tree)

2022. 12. 15. 18:47·Data structure

AVL 트리

트리 내의 어떤 노드도 좌서브 트리와 우서브 트리의 높이 차가 1보다 크지 않은 상태로 유지되는 이진 검색 트리


AVL 트리의 회전

1. 좌회전

def rotateLeft(self, t):
	tR = t.right; tRL = tR.left
   tR.left = t; t.right = tRL
   t.height = max(t.left.height, t.right.height) + 1
   tR.height = max(tR.left.height, tR.right.height) + 1
   # 밑에서 부터 올라가면서 높이를 수정해 주어야 한다!
   return tR



2. 우회전

def rotateRight(self, t):
	tL = t.left; tLR = tL.right
   tL.right = t; t.left = tLR
   t.height = max(t.left.height, t.right.height) + 1
   tL.height = max(tL.left.height, tL.right.height) + 1
   return tL




AVL 트리의 수선 (균형 맞추기)

t를 루트로 하는 트리의 수선 작업은 t의 네 손자 서브 트리 중 가장 깊은 것에 따라 다음 네 가지 유형으로 나뉜다.

LL : t.left.left가 가장 깊음
LR : t.left.right 가 자장 깊음
RR : t.right.right가 가장 깊음
RL : t.right.left가 가장 깊음

LL유형의 해결 : t를 기준으로 우회전한다.
LR유형의 해결 : t.left를 기준으로 좌회전해서 LL유형으로 만든 다음, t를 기준으로 우회전한다.
RR유형의 해결 : t를 기준으로 좌회전한다.
RL유형의 해결 : t.left를 기준으로 우회전해서 RR유형으로 만든 다음 t를 기준으로 좌회전한다.

 

# 균형 맞추기
    def bf(self, t):
        return t.left.height - t.right.height
        # 양수이면 L비대, 음수이면 R비대
    def rotateLeft(self, t):
        tR = t.right; tRL = tR.left
        tR.left = t; t.right = tRL
        t.height = max(t.left.height, t.right.height) + 1
        tR.height = max(tR.left.height, tR.right.height) + 1
        # 밑에서 부터 올라가면서 높이를 수정해 주어야 한다!
        return tR
    def rotateRight(self, t):
        tL = t.left; tLR = tL.right
        tL.right = t; t.left = tLR
        t.height = max(t.left.height, t.right.height) + 1
        tL.height = max(tL.left.height, tL.right.height) + 1
        return tL
    def getBalance(self, t):
        if self.bf(t) > 1:  # L 유형
            if self.bf(t.left) < 0:  # LR
                t.left = self.rotateLeft(t.left)
            return self.rotateRight(t)  # LL
        elif self.bf(t) < -1:  # R 유형
            if self.bf(t.right) > 0:  # RL
                t.right = self.rotateRight(t.right)
            return self.rotateLeft(t)  # RR
        else:  # 수선 불필요
            return t

왼쪽 : height, 오른쪽 괄호 : bf


bf > 1 : L 유형
bf < -1 : R 유형


None 대신 경계 노드 NIL로 처리하면 일반성이 높아진다.
노드 x가 None이라 해도 NIL.height (-1)가 존재하며, 서브트리의 높이 계산을 위한 필드 height를 쉽게 처리할 수 있다.

삽입과 삭제 과정에서 높이에 변화가 생길 가능성이 있는 모든 곳에서 AVL 균형을 체크하고, 만일 균형이 깨지면 균형 맞추기 작업을 수행한다.
검색 과정에서는 AVL 균형이 깨지지 않는다.



AVL 트리의 구현

class AVLNode:
    def __init__(self, item, left, right, height):
        self.item = item
        self.left = left
        self.right = right
        self.height = height
class AVLTree:
    def __init__(self):
        self.NIL = AVLNode(None, None, None, -1)  # NIL의 height는 -1로, 리프노드의 height는 0으로 한다.
        self.__root = self.NIL
    
    # 균형 맞추기
    def bf(self, t):
        return t.left.height - t.right.height
        # 양수이면 L비대, 음수이면 R비대
    def rotateLeft(self, t):
        tR = t.right; tRL = tR.left
        tR.left = t; t.right = tRL
        t.height = max(t.left.height, t.right.height) + 1
        tR.height = max(tR.left.height, tR.right.height) + 1
        # 밑에서 부터 올라가면서 높이를 수정해 주어야 한다!
        return tR
    def rotateRight(self, t):
        tL = t.left; tLR = tL.right
        tL.right = t; t.left = tLR
        t.height = max(t.left.height, t.right.height) + 1
        tL.height = max(tL.left.height, tL.right.height) + 1
        return tL
    def getBalance(self, t):
        if self.bf(t) > 1:  # L 유형
            if self.bf(t.left) < 0:  # LR
                t.left = self.rotateLeft(t.left)
            return self.rotateRight(t)  # LL
        elif self.bf(t) < -1:  # R 유형
            if self.bf(t.right) > 0:  # RL
                t.right = self.rotateRight(t.right)
            return self.rotateLeft(t)  # RR
        else:  # 수선 불필요
            return t
    # 원소 검색 : 검색 과정에서는 AVL 균형이 깨지지 않는다.
    def search(self, x):
        return self.__searchItem(self.__root, x)
    def __searchItem(self, tNode, x):
        if tNode == self.NIL or x == tNode.item:
            return tNode
        elif x < tNode.item:
            return self.__searchItem(tNode.left, x)
        else:
            return self.__searchItem(tNode.right, x)
    # 원소 삽입 : 높이에 변화가 생길 가능성이 있는 모든 곳에서 균형을 체크하고 만일 균형이 깨지면 균형 맞추기 작업을 수행한다
    def insert(self, x):
        self.__root = self.__insertItem(self.__root, x)
    def __insertItem(self, tNode, x):
        if tNode == self.NIL:  # self.NIL의 item값이 None이므로 int인 x값과 직접 비교가 불가능 하므로 여기서 걸러져야 함!!!
            tNode = AVLTree(x, self.NIL, self.NIL, 0)
        elif x < tNode.item:
            tNode.left = self.__insertItem(tNode.left, x)
            tNode.height = max(tNode.left.height, tNode.right.height) + 1
            tNode = self.getBalance(tNode)
        else:
            tNode.right = self.__insertItem(tNode.right, x)
            tNode.height = max(tNode.left.height, tNode.right.height) + 1
            tNode = self.getBalance(tNode)
        return tNode

    # 원소 삭제 : 높이에 변화가 생길 가능성이 있는 모든 곳에서 균형을 체크하고 만일 균형이 깨지면 균형 맞추기 작업을 수행한다
    def delete(self, x):
        self.__root = self.__deleteItem(self.__root, x)
    def __deleteItem(self, tNode, x):
        if tNode == self.NIL:
            return self.NIL
        elif x == tNode.item:
            tNode = self.__deleteNode(tNode)
            # 이미 height를 수정한 tNode가 tNode자리에 들어오는 것이기 때문에 따로 height를 조정할 필요는 없다.
        elif x < tNode.item:
            tNode.left = self.__deleteItem(tNode.left, x)
            tNode.height = max(tNode.left.height, tNode.right.height) + 1
            tNode = self.getBalance(tNode)
        else:
            tNode.right = self.__deleteItem(tNode.right, x)
            tNode.height = max(tNode.left.height, tNode.right.height) + 1
            tNode = self.getBalance(tNode)
        return tNode 
    def __deleteNode(self, tNode):
        if tNode.left == self.NIL and tNode.right == self.NIL:
            return self.NIL
        elif tNode.left == self.NIL:
            return tNode.right
        elif tNode.right == self.NIL:
            return tNode.left
        else:
            (rtnItem, rtnNode) = self.__deleteMinItem(tNode.right)
            tNode.item = rtnItem
            tNode.right = rtnNode
            tNode.height = max(tNode.left.height, tNode.right.height) + 1
            tNode = self.getBalance(tNode)
            return tNode
    def __deleteMinItem(self, tNode):
        if tNode.left == None:
            return (tNode.item, tNode.right)
        else:
            (rtnItem, rtnNode) = self.__deleteMinItem(tNode.left)
            tNode.left = rtnNode
            tNode.height = max(tNode.left.height, tNode.right.height) + 1
            tNode = self.getBalance(tNode)
            return (rtnItem, tNode)
    # 기타 작업 : isEmpty, clear
    def isEmpty(self):
        return self.__root == self.NIL
    def clear(self):
        self.__root = self.NIL
'Data structure' 카테고리의 다른 글
  • [DS] 힙 (Heap) - Priority Queue ☑️
  • [DS] 이진 검색 트리 (Binary Search Tree)
  • [DS] FIFO 큐 (FIFO Queue) ☑️
  • [DS] 스택 (Stack) ☑️
vysryoo
vysryoo
  • vysryoo
    vysryoo
    vysryoo
  • 전체
    오늘
    어제
    • 분류 전체보기 (129)
      • Python (20)
      • Data structure (12)
      • Algorithm (14)
      • Operating system (18)
      • Programming language theory (12)
      • Computer architecture (6)
      • Softeware engineering (8)
      • Multicore (2)
      • Data Base (3)
      • Problem solving (24)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
vysryoo
[DS] AVL 트리 (AVL Tree)
상단으로

티스토리툴바