- 각 노드에 값이 있다.
- 값들은 전순서가 있다.
- 노드의 왼쪽 서브트리에는 그 노드의 값보다 작은 값들을 지닌 노드들로 이루어져 있다.
- 노드의 오른쪽 서브트리에는 그 노드의 값과 같거나 큰 값들을 지닌 노드들로 이루어져 있다.
- 좌우 하위 트리는 각각이 다시 이진 탐색 트리여야 한다.
-
이진 트리의 각 노드들에 대한 클래스
class Node(object): def __init__(self, data): self.data = data self.left = self.right = None
-
위의 Node 인스턴스들로 구현될 이진 트리 클래스. root 노드가 존재하며 insert, find, delete 메소드를 포함해야 한다.
class BinarySearchTree(object): def __init__(self): self.root = None def insert(self, data): ... def find(self, data): ... def delete(self, data): ...
이진탐색트리의 탐색 시간은 트리의 높이 h만큼 소요된다. 즉 O(h)이다.
def find(self, data):
return self.find_node(self.root, data)
def find_node(self, node, data):
if node is None: # 리프노드 도달
return False
elif node.data == data: # 현재 노드값이 찾는 값이라면 True
return True
elif node.data > data: # 왼쪽 자식으로 넘어가기
return self.find_node(node.left, data)
else: # 오른쪽 자식으로 넘어가기
return self.find_node(node.right, data)
트리를 탐색하여 마지막 리프노드에 값을 입력해주므로 시간복잡도는 find와 마찬가지로 O(h)이다. insert로 리프노드가 추가되어도 트리의 속성이 깨지지 않는다. 트리의 맨 왼쪽과 맨 오른쪽 노드는 각각 최소값과 최대값을 갖는다.
def insert(self, data):
if self.root is None: # 루트가 없다면 본 데이터로 루트를 만들어준다.
self.root = Node(data)
else: # 루트부터 탐색하여 데이터 입력
self.insert_node(self.root, data)
def insert_node(self, node, data):
if node.data > data:
if node.left is None:
node.left = Node(data)
else:
self.insert_node(node.left, data)
elif node.data < data:
if node.right is None:
node.right = Node(data)
else:
self.insert_node(node.right, data)
# 현재 노드값이 data와 같을경우, 이미 트리에 값이 있으므로 아무것도 안 함
#else:
# pass
삭제하려는 노드의 자식 수에 따라 삭제 방법이 달라진다.
- 자식노드가 없는 노드(리프 노드) 삭제 : 해당 노드를 단순히 삭제한다.
- 자식노드가 1개인 노드 삭제 : 해당 노드를 삭제하고 그 위치에 해당 노드의 자식노드를 대입한다.
- 자식노드가 2개인 노드 삭제 : 다음 두 가지 방법 중 하나를 택할 수 있다.
- 왼쪽 서브트리에서 가장 값이 큰 노드 를 제거한 뒤, 삭제할 노드의 값을 본 값으로 대체한다.
- 오른쪽 서브트리에서 값이 가장 작은 노드 를 제거한 뒤, 삭제할 노드의 값을 본 값으로 대체한다.
이진탐색트리의 모든 노드가 오른쪽 자식만 가지거나 왼쪽 자식만 가져서 트리의 모양이 일자로 늘어설 경우, 탐색에 소요되는 시간은 O(log n)이 아니라 O(n)이 될 수 있다.
이러한 문제를 보완삭제 단계에서 트리의 균형을 보완하는 AVL Tree 등이 존재한다.