티스토리 뷰

이진탐색트리(Binary Search Tree)


이진탐색 트리에서 원소 삭제

 

1. 키(Key)를 이용해서 노드를 찾는다.

  • 해당 키의 노드가 없으면, 삭제할 것도 없다.
  • 찾은 노드의 부모 노드도 알고 있어야 한다.

 

2. 찾은 노드를 제거하고도 이진탐색 트리 성질을 만족하도록 트리의 구조를 정리한다.

 


인터페이스의 설계

✔ 입력 : 키(Key)

✔ 출력: 삭제한 경우 True,  해당 키의 노드가 없는 경우 False

 

class BinarySearchTree:
	def remove(self, key):
    	node, parent = self.lookup(key)
        if node:
        	...
            return True
        else:
        	return False

 


이진탐색 트리 구조의 유지

삭제되는 노드가,

1. 말단(leaf) 노드인 경우

2. 자식 노드를 하나 가지고 있는 경우

3. 자식 노드를 둘 가지고 있는 경우

 

[ 말단(leaf) 노드인 경우 ]

해당 노드를 없애면 된다.

→ 부모 노드의 링크를 조정한다 

 

[ 자식 노드를 하나 가지고 있는 경우 ]

삭제되는 노드 자리에 해당 노드의 자식 노드를 대신 배치한다.

→  자식노드가 왼쪽에 존재하는지/오른쪽에 존재하는지.

→  부모 노드의 링크를 조정한다.

 

 

[ 자식 노드를 둘 가지고 있는 경우 ]

삭제되는 노드보다 바로 다음 (큰) 키를 가지는 노드를 찾아 그 노드를 삭제되는 노드 자리에 대신 배치하고 해당 노드를 삭제한다.

 

 

 

 


이진 탐색 트리가 효율적이지 못한 경우

한쪽으로 치우치는 경우 효율적이지 못하다.

 


보다 나은 성능을 보이는 이진탐색 트리들

높이의 균형을 유지함으로써 O(logn)의 탐색 복잡도를 보장한다. 

삽입, 삭제 연산이 보다 복잡해질 수 있다.

 

  • AVL Tree
  • Red-black Tree

문1. 이진탐색트리에서 노드의 삭제 연산 구현

 

class Node:

    def __init__(self, key, data):
        self.key = key
        self.data = data
        self.left = None
        self.right = None


    def insert(self, key, data):
        if key < self.key:
            if self.left:
                self.left.insert(key, data)
            else:
                self.left = Node(key, data)
        elif key > self.key:
            if self.right:
                self.right.insert(key, data)
            else:
                self.right = Node(key, data)
        else:
            raise KeyError('Key %s already exists.' % key)


    def lookup(self, key, parent=None):
        if key < self.key:
            if self.left:
                return self.left.lookup(key, self)
            else:
                return None, None
        elif key > self.key:
            if self.right:
                return self.right.lookup(key, self)
            else:
                return None, None
        else:
            return self, parent


    def inorder(self):
        traversal = []
        if self.left:
            traversal += self.left.inorder()
        traversal.append(self)
        if self.right:
            traversal += self.right.inorder()
        return traversal


    def countChildren(self):
        count = 0
        if self.left:
            count += 1
        if self.right:
            count += 1
        return count


class BinSearchTree:

    def __init__(self):
        self.root = None


    def insert(self, key, data):
        if self.root:
            self.root.insert(key, data)
        else:
            self.root = Node(key, data)


    def lookup(self, key):
        if self.root:
            return self.root.lookup(key)
        else:
            return None, None


    def remove(self, key):
        node, parent = self.lookup(key)
        if node:
            nChildren = node.countChildren()
            # The simplest case of no children
            if nChildren == 0:
                # 만약 parent 가 있으면
                # node 가 왼쪽 자식인지 오른쪽 자식인지 판단하여
                # parent.left 또는 parent.right 를 None 으로 하여
                # leaf node 였던 자식을 트리에서 끊어내어 없앱니다.
                if parent:
                    if parent.left == node:
                        parent.left = None
                    else:
                        parent.right = None
                # 만약 parent 가 없으면 (node 는 root 인 경우)
                # self.root 를 None 으로 하여 빈 트리로 만듭니다.
                else:
                    self.root = None
            # When the node has only one child
            elif nChildren == 1:
                # 하나 있는 자식이 왼쪽인지 오른쪽인지를 판단하여
                # 그 자식을 어떤 변수가 가리키도록 합니다.
                if node.left:
                    temp = node.left
                else:
                    temp = node.right
                # 만약 parent 가 있으면
                # node 가 왼쪽 자식인지 오른쪽 자식인지 판단하여
                # 위에서 가리킨 자식을 대신 node 의 자리에 넣습니다.
                if parent:
                    if parent.left == node:
                        parent.left = temp
                    else:
                        parent.right = temp
                # 만약 parent 가 없으면 (node 는 root 인 경우)
                # self.root 에 위에서 가리킨 자식을 대신 넣습니다.
                else:
                    self.root = temp
            # When the node has both left and right children
            else:
                parent = node
                successor = node.right
                # parent 는 node 를 가리키고 있고,
                # successor 는 node 의 오른쪽 자식을 가리키고 있으므로
                # successor 로부터 왼쪽 자식의 링크를 반복하여 따라감으로써
                # 순환문이 종료할 때 successor 는 바로 다음 키를 가진 노드를,
                # 그리고 parent 는 그 노드의 부모 노드를 가리키도록 찾아냅니다.
                while successor.left:
                    parent = successor
                    successor = successor.left
                # 삭제하려는 노드인 node 에 successor 의 key 와 data 를 대입합니다.
                node.key = successor.key
                node.data = successor.data
                # 이제, successor 가 parent 의 왼쪽 자식인지 오른쪽 자식인지를 판단하여
                # 그에 따라 parent.left 또는 parent.right 를
                # successor 가 가지고 있던 (없을 수도 있지만) 자식을 가리키도록 합니다.
                if parent.left == successor:
                    parent.left = successor.right
                else:
                    parent.right = successor.right

            return True

        else:
            return False


    def inorder(self):
        if self.root:
            return self.root.inorder()
        else:
            return []


def solution(x):
    return 0
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함