트리구조란 그래프의 일종으로, 한 노드에서 시작해서 다른 정점들을 순회하여 자기 자신에게 돌아오는 순환이 없는 연결그래프이다. - wikipedia
각 노드의 차수가 2이하인 트리
pre-order : root -> left -> right
def preorder(root):
if root:
print(root.data) # root
if root.left: preorder(root.left) # left
if root.right: preorder(root.right) # right
in-order : left -> root -> right
def inorder(root):
if root:
if root.left: inorder(root.left) # left
print(root.data) # root
if root.right: inorder(root.right) # right
post-order : left -> right -> root
def postorder(root):
if root:
if root.left: postorder(root.left) # left
if root.right: postorder(root.right) # right
print(root.data) # root
level-order : 큐를 사용하여 각 노드를 레벨순으로 순회
from collections import deque
def level_order(root):
queue = deque.([root])
while queue:
root = queue.popleft() # dequeue root node
if root:
print(root.data) # traverse root node
if root.left:
queue.append(root.left) # enqueue left subtree
if root.right:
queue.append(root.right) # enqueue right subtree
left child < root < right child 를 만족하는 이진트리
탐색
삽입
삭제
직접 구현
class Node:
def __init__(self, data):
self.left = None
self.right = None
self.data = data
class BST:
def __init__(self):
self.root = None
def insert(self, data):
self.root = self._insert_value(self.root, data)
return self.root is not None
def _insert_value(self, node, data):
if node is None:
node = Node(data) # node를 생성
else:
if data <= node.data: # data가 현재노드의 값보다 작거나 같으면
node.left = self._insert_value(node.left, data) # left child로 이동
else: # data가 현재노드의 값보다 크면
node.right = self._insert_value(node.right, data) # right child로 이동
return node
def search(self, key):
return self._search_value(self.root, key)
def _search_value(self, root, key):
if root is None or root.data == key: # 현재 노드가 찾는 값이면
return root is not None # 현재 노드를 반환
elif key < root.data: # 찾는 값이 현재 노드의 값보다 작으면
return self._search_value(root.left, key) # left child로 이동
else: # 찾는 값이 현재 노드의 값보다 크면
return self._search_value(root.right, key) # right child로 이동
def delete(self, key):
self.root, deleted = self._delete_value(self.root, key)
return deleted
def _delete_value(self, node, key):
if node is None: # 만약 찾는 노드가 없으면
return node, False # False를 반환
deleted = False
if key == node.data: # 삭제하려는 노드를 찾으면
deleted = True # True를 반환
if node.left and node.right: # child가 둘 다 있으면
parent, child = node, node.right # 현재 노드의 right child의 left leaf를 찾고
while child.left is not None:
parent, child = child, child.left
child.left = node.left # 현재 노드로 올림
if parent != node:
parent.left = child.right # 올리기 전에 right child를 parent의 left child로 연결
child.right = node.right
node = child
elif node.left or node.right: # child가 하나만 있으면
node = node.left or node.right # child를 현재 노드로 올림
else: # child가 없으면
node = None # 현재 노드 삭제
elif key < node.data: # 만약 찾는 값이 현재 값보다 작으면
node.left, deleted = self._delete_value(node.left, key) # left child로 이동
else: # 만약 찾는 값이 현재 값보다 크면
node.right, deleted = self._delete_value(node.right, key) # right child로 이동
return node, deleted
bisect 모듈 : 정렬된 배열에서 특정 원소를 찾는 모듈
힙은 최댓값 및 최솟값을 찾아내는 연산을 빠르게 하기 위해 고안된 완전 이진 트리 를 기본으로 한 자료구조이다. - wikipedia
heaqq 모듈 : 우선순위 큐 알고리즘 제공(최소 힙)
import heapq
heap = [] # 빈 리스트 생성
heapq.heappush(heap, 10) # heappush(a, x) : 힙 a에 x 삽입
heapq.heappush(heap, 6)
heapq.heappop(heap) # heappop(a) : 힙 a의 루트노드 반환 후 삭제
heapq.heapify(heap) # heapify(a) : 리스트 a를 힙으로 변환
# 최대 힙은 키값의 부호를 바꿔주는 방법으로 사용
직접 구현 : 배열을 이용하여 구현. 자식노드 인덱스 // 2 = 부모노드 인덱스
class Heap:
def __init__(self):
self.heap = []
self.heap.append(None) # index는 1번부터 시작
def check_swap_up(self,idx):
if idx <= 1: # parent가 없으면
return False # False 반환
parent_idx = idx // 2 # parent index 계산
if self.heap[idx] > self.heap[parent_idx]: # 부모 노드보다 값이 크면
return True # True 반환(swap o)
else: # 부모 노드보다 값이 작으면
return False # False 반환(swap x)
def insert(self, data):
self.heap.append(data) # 힙의 맨 뒤에 추가
idx = len(self.heap) - 1
while self.check_swap_up(idx): # 부모 노드보다 값이 크면
parent_idx = idx // 2 # 두 노드의 위치를 바꿈
self.heap[idx], self.heap[parent_idx] = self.heap[parent_idx], self.heap[idx]
idx = parent_idx
return True
def check_swap_down(self, idx):
left_idx = idx * 2
right_idx = idx * 2 + 1
if left_idx >= len(self.heap): # child가 없으면
return False # False 반환(swap x)
elif right_idx >= len(self.heap): # left child만 있으면
if self.heap[left_idx] > self.heap[idx]: # left child가 부모 노드의 값보다 크면
self.flag = 1
return True # True 반환(swap o)
else: # left child가 부모 노드의 값보다 작으면
return False # False 반환(swap x)
else: # 양쪽 child가 모두 있으면
if self.heap[left_idx] > self.heap[right_idx]: # left child가 right child보다 크면
if self.heap[left_idx] > self.heap[idx]: # left child가 부모 노드의 값보다 크면
self.flag = 1
return True # True 반환(swap o)
else: # left child가 부모 노드의 값보다 작으면
return False # False 반환(swap x)
else: # left child가 right child보다 작으면
if self.heap[right_idx] > self.heap[idx]: # right child가 부모 노드의 값보다 크면
self.flag = 2
return True # True 반환(swap o)
else: # right child가 부모 노드의 값보다 작으면
return False # False 반환(swap x)
def pop(self):
if len(self.heap) <= 1: # parent가 없으면
return None # None을 반환
max = self.heap[1] # root의 값을 꺼냄
self.heap[1] = self.heap[-1] # 마지막 노드를 root로 올림
del self.heap[-1] # 맨 뒤 노드 삭제
idx = 1
self.flag = 0 # 0 = False, 1 = left child와 swap, 2 = right child와 swap
while self.check_swap_down(idx): # 자식 노드가 부모 노드보다 값이 크면
left_idx = idx * 2
right_idx = idx * 2 + 1
if self.flag == 1: # left child와 swap
self.heap[idx], self.heap[left_idx] = self.heap[left_idx], self.heap[idx]
idx = left_idx
elif self.flag == 2: # right child와 swap
self.heap[idx], self.heap[right_idx] = self.heap[right_idx], self.heap[idx]
idx = right_idx
return max # 아까 꺼낸 root의 값을 반환