트리란, 데이터의 상-하 관계(계층적 관계)를 저장하는 자료구조이다.
트리에서 상위는 부모, 하위는 자식으로 부른다.
그중 가장 위에 해당하는 노드를 root
노드라고 부른다.
각 노드가 최대 2개의 자식 노드를 가질 수 있는 트리
2개 밖에 없기 때문에, 왼쪽 자식 & 오른쪽 자식으로 구별해서 부른다.
위와 같은 트리를 구현해보자.
class Node:
"""이진 트리 노드 클래스"""
def __init__(self, data):
self.data = data
self.left_child = None
self.right_child = None
# root 노드 생성
root_node = Node("A")
node_B = Node("B")
node_C = Node("C")
node_D = Node("D")
node_E = Node("E")
node_F = Node("F")
node_G = Node("G")
node_H = Node("H")
root_node.left_child = node_B
root_node.right_child = node_C
node_B.left_child = node_D
node_B.right_child = node_E
node_E.left_child = node_G
node_E.right_child = node_H
node_C.right_child = node_F
정 이진 트리(Full Binary Tree)는 모든 자식노드가 0개 또는 2개로만 이루어진 이진트리를 말한다.
이진 트리 중에서도 마지막 레벨 직전의 레벨까지는 모든 노드들이 다 채워진 트리를 “완전 이진 트리”라고 한다. 또, 마지막 레벨에서는 노드들이 다 채워질 필요는 없더라도, 왼쪽부터 오른쪽 방향으로는 노드들이 다 채워져야 한다.
아래는 완전 이진트리다.
아래는 완전 이진트리가 아닌 경우다.
def get_parent_index(complete_binary_tree, index):
"""배열로 구현한 완전 이진 트리에서 index번째 노드의 부모 노드의 인덱스를 리턴하는 함수"""
# 여기에 코드를 작성하세요
if index > 1:
return index//2
def get_left_child_index(complete_binary_tree, index):
"""배열로 구현한 완전 이진 트리에서 index번째 노드의 왼쪽 자식 노드의 인덱스를 리턴하는 함수"""
if len(complete_binary_tree) > index*2:
return index*2
def get_right_child_index(complete_binary_tree, index):
"""배열로 구현한 완전 이진 트리에서 index번째 노드의 오른쪽 자식 노드의 인덱스를 리턴하는 함수"""
if len(complete_binary_tree) > index*2+1:
return index*2+1
# 테스트 코드
root_node_index = 1 # root 노드
tree = [None, 1, 5, 12, 11, 9, 10, 14, 2, 10] # 과제 이미지에 있는 완전 이진 트리
# root 노드의 왼쪽과 오른쪽 자식 노드의 인덱스를 받아온다
left_child_index = get_left_child_index(tree, root_node_index)
right_child_index = get_right_child_index(tree,root_node_index)
print(tree[left_child_index])
print(tree[right_child_index])
# 9번째 노드의 부모 노드의 인덱스를 받아온다
parent_index = get_parent_index(tree, 9)
print(tree[parent_index])
# 부모나 자식 노드들이 없는 경우들
parent_index = get_parent_index(tree, 1) # root 노드의 부모 노드의 인덱스를 받아온다
print(parent_index)
left_child_index = get_left_child_index(tree, 6) # 6번째 노드의 왼쪽 자식 노드의 인덱스를 받아온다
print(left_child_index)
right_child_index = get_right_child_index(tree, 8) # 8번째 노드의 오른쪽 자식 노드의 인덱스를 받아온다
print(right_child_index)
포화 이진 트리는 위 트리처럼 모든 레벨이 빠짐없이 다 노드로 채워져있는 이진 트리이다.
순회 : 자료구조에 저장된 모든 데이터를 도는 것
트리에 저장된 모든 데이터를 출력해보도록 하자.
선형적 자료구조를 순회할 때는 보통 반복문을 사용하면 된다.
트리에서는 앞과 뒤라는 선형적인 관계가 없기 때문에 조금 다른 방식을 사용한다. 트리를 순회할 때는 주로 재귀함수를 사용한다.
순회 기본 동작
트리를 순회하는 대표적인 방법 3가지
: 부분 트리를 순회하기 전에 현재 노드를 출력하는 방법이다.
1. 현재 노드 데이터를 출력
2. 재귀적으로 왼쪽 부분 트리 순회
3. 재귀적으로 오른쪽 부분 트리 순회
pre-order의 출력 순서를 살펴보자. 위 순서를 재귀적으로 호출하는 것을 알 수 있다.
: 부분트리를 순회한 후에 현재 노드를 출력하는 방법이다.
1. 재귀적으로 왼쪽 부분 트리 순회
2. 재귀적으로 오른쪽 부분 트리 순회
3. 현재 노드 데이터를 출력
post-order 출력 순서를 살펴보자.
: 부분트리 순회 사이에 현재 노드를 출력하는 방법이다.
1. 재귀적으로 왼쪽 부분 트리 순회
2. 현재 노드 데이터를 출력
3. 재귀적으로 오른쪽 부분 트리 순회
in-order 출력 순서를 살펴보자.
class Node:
"""이진 트리 노드를 나타내는 클래스"""
def __init__(self, data):
"""이진 트리 노드는 데이터와 두 자식 노드에 대한 레퍼런스를 갖는다"""
self.data = data
self.left_child = None
self.right_child = None
def traverse_inorder(node):
"""in-order 순회 함수"""
if node is not None:
traverse_inorder(node.left_child) # 1. 왼쪽 부분 트리를 in-order 순회한다
print(node.data) # 2. 현재 노드의 데이터를 출력한다.
traverse_inorder(node.right_child) # 3. 오른쪽 부분 트리를 in-order 순회한다.
# 여러 노드 인스턴스 생성
node_A = Node("A")
node_B = Node("B")
node_C = Node("C")
node_D = Node("D")
node_E = Node("E")
node_F = Node("F")
node_G = Node("G")
node_H = Node("H")
node_I = Node("I")
# 생성한 노드 인스턴스들 연결
node_F.left_child = node_B
node_F.right_child = node_G
node_B.left_child = node_A
node_B.right_child = node_D
node_D.left_child = node_C
node_D.right_child = node_E
node_G.right_child = node_I
node_I.left_child = node_H
# 노드 F를 root 노드로 만든다
root_node = node_F
# 만들어 놓은 트리를 in-order로 순회한다
traverse_inorder(root_node)
이렇게 트리를 순회하게 되면, 노드들 사이에 선형적인 순서
를 만들 수 있다.
특정 방식으로 데이터가 순서대로 출력되므로, 선형적 관계를 만들고 활용할 수 있다.