- 뿌리는 트리에서 가장 윗부분으로 루트노드를 말한다(그림에서는 A).
- 잎은 트리를 구성하고 있는 요소인 노드를 말한다(그림에서는 A, B, C, D, E, F, G, H)
- 노드 : 트리를 구성하고 있는 요소를 말하며 그림에서는 A, B, C, D, E, F, G, H를 말한다.
- 루트코드 : 트리의 가장 윗부분을 말하며 그림에선느 A 노드를 말한다.
- 간선 : 노드와 노드를 연결하고 있는 선을 말한다.
- 부모노드 : 노드와 간선으로 연결된 윗부분 노드를 말한다.
- 자식노드 : 노드와 간선으로 연결된 아랫부분 노드를 말한다.
EX) B의 자식노드는 D, E이다. D, E의 부모노드는 B이다.
- 리프노드 : 자식노드가 없는 노드를 말하며 그림에서는 H, E, F, G가 있다.
- 서브 트리 : 하나의 노드와 그 노드들으 ㅣ자손들로 이루어진 트리이다.
- 레벨 : 트리의 각 계층을 의미한다.
(A의 레벨이 1이고 B, C의 레벨이 2이다. 그리고 D, E, F, G의 레벨이 3, H의 레벨이 4이다).
- 높이 : 트리의 높이를 말하며, 그림의 경우 높이는 4이다.
트리를 쓰는 이유는 특정 원소를 찾기 위해 ()의 시간복잡도를 사용하기 위해서다. 따라서 각 노드의 개수가 전부 1개라면 트리를 쓰는 이유가 사라지며 이는 배열을 사용하는 것과 똑같은 효과를 낸다.
- 마지막 레벨을 제외한 나머지 레벨에서는 노드들이 자식노드를 2개씩 가진다.
- 마지막 레벨은 왼쪽부터 노드가 채워져 있어야 한다.
[문제]
이진트리를 입력받아 전위 순회(preorder traversal), 중위 순회(inorder traversal), 후위 순회(postorder traversal)한 결과를 출력하는 프로그램을 작성하시오.예를 들어 위와 같은 이진트리가 입력되면,
- 전위 순회한 결과 : ABCDEFG // (루트) (왼쪽 자식) (오른쪽 자식) - 중위 순회한 결과 : DBAECFG // (왼쪽 자식) (루트) (오른쪽 자식) - 후위 순회한 결과 : DBEGFCA // (왼쪽 자식) (오른쪽 자식) (루트)
가 된다.
[입력]
첫째 줄에는 이진트리의 노드의 개수 N(1 <= N <= 26)이 주어진다. 둘째 줄부터 N개의 줄에 걸쳐 각 노드와 그의 왼쪽 자식노드, 오른쪽 자식노드가 주어진다. 노드의 이름은 A부터 차례대로 영문자 대문자로 매겨지며, 항상 A가 루트노드가 된다. 자식노드가 없는 경우에는 .으로 표현된다.
[출력]
첫째 줄에 전위 순회, 둘쨰 줄에 중위 순회, 셋째 줄에 후위 순회한 결과를 출력한다. 각 줄에 N개의 알파뱃을 공백없이 출력하면 된다.
예제 입력 1 예제 출력 1 7 ABCDEFG A B C DBAECFG B D . DBEGFCA C E F E . . F . G D . . G . .
문제를 해석하기 위해 전위 순회, 중위 순회, 후위 순회를 알 필요가 있다.
- 전위 순회 : 2 -> 1 -> 3
- 중위 순회 : 1 -> 2 -> 3
- 후위 순회 : 1 -> 3 -> 2
위 트리를 기준으로 보자면 다음과 같다.
- 전위 순회 : 4 -> 2 -> 1 -> 3 -> 5 -> 6 -> 7
- 중위 순회 : 1 -> 2 -> 3 -> 4 -> 6 -> 7
- 후위 순회 : 1 -> 3 -> 2 -> 6 -> 7 -> 5 -> 4
이렇게 3가지의 탐색방법을 이해한다면 예제 입력의 탐색도 이해할 수 있을 것이다.
class Node:
def __init__(node, data, left_node, right_node):
node.data = data
node.left_node = left_node
node.right_node = right_node
def pre_order(node):
print(node.data, end='')
if node.left_node != '.':
pre_order(tree[node.left_node])
if node.right_node != '.':
pre_order(tree[node.right_node])
def in_order(node):
if node.left_node != '.':
in_order(tree[node.left_node])
print(node.data, end = '')
if node.right_node != '.':
in_order(tree[node.right_node])
def post_order(node):
if node.left != '.':
post_order(tree[node.left_node])
if node.right_node != '.':
post_order(tree[node.right_node])
print(node.data, end='')
n = int(input())
tree = {}
for _ in rnage(n):
data, left_node, right_node = input().split(' ')
tree[data] = Node(data, left_node, right_node)
pre_order(tree['A'])
print()
in_order(tree['A'])
print()
post_order(tree['A'])
class Node:
def __init__(node, data, left_node, right_node):
node.data = data
node.left_node = left_node
node.right_node = right_node
def pre_order(node): # 전위 순회 함수를 생성한다.
print(node.data, end='')
if node.left_node != '.':
pre_order(tree[node.left_node])
if node.right_node != '.':
pre_order(tree[node.right_node])
def in_order(node): # 중위 순회 함수를 생성한다.
if node.left_node != '.':
in_order(tree[node.left_node])
print(node.data, end = '')
if node.right_node != '.':
in_order(tree[node.right_node])
def post_order(node): # 후위 순회 함수를 생성한다.
if node.left != '.':
post_order(tree[node.left_node])
if node.right_node != '.':
post_order(tree[node.right_node])
print(node.data, end='')
- 모든 노드의 Key값은 유일하다.
=> 이진 검색 트리가 Key값이 유일한 것이지 다른 트리는 대게 Key값이 유일하지 않다.
- 왼쪽 서브 트리의 키들은 루트의 Key값보다 작다.
- 오른쪽 서브 트리의 키들은 루트의 Key값보다 크다.
- 노드의 자식노드는 최대 2개가 올 수 있다.
- 왼쪽과 오른쪽 서브 트리도 이진 검색 트리이다.
[문제]
이진 검색 트리는 다음과 같은 세 가지 조건을 만족하는 이진 트리이다.- 노드의 왼쪽 서브트리에 있는 모든 노드의 키는 노드의 키보다 작다. - 노드의 왼쪽 서브트리에 있는 모든 노드의 키는 노드의 키보다 크다. - 왼쪽, 오른쪽 서브트리도 이진 검색 트리이다.
전위 순회(루트 - 왼쪽 - 오른쪽)는 루트를 방문하고, 왼쪽 서브트리, 오른쪽 서브트리를 순서대로 방문하면서 노드의 키를 출력한다. 후위 순회(왼쪽 - 오른쪽 - 루트)는 왼쪽 서버트리, 오른쪽 서브트리, 루트노드 순서대로 키를 출력한다. 예를 들어, 위의 이진 검색 트리의 전위 순회 결과는 50 30 24 5 28 45 98 52 60 이고, 후위 순회 결과는 5 28 24 45 30 60 52 98 50 이다.
이진 검색 트리를 전위 순회한 결과가 주어졌을 때, 이 트리를 후위 순회한 결과를 구하는 프로그램을 작성하시오.
[입력]
트리를 전위 순회한 결과가 주어진다. 노드에 들어있는 키의 값은 보다 작은 양의 정수이다. 모든 값은 한 줄에 하나씩 주어지며, 노드의 수는 10,000개 이하이다. 같은 키를 가지는 노드는 없다.
[출력]
입력으로 주어진 이진 검색 트리를 후위 순회한 결과를 한 줄에 하나씩 출력한다.
예제 입력 1 예제 출력 1 50 5 30 28 24 24 5 45 28 30 45 60 98 52 52 98 69 50
1. 노드를 방문한다.
2. 왼쪽 서브 트리를 전위 순회한다.
3. 오른쪽 서브 트리를 전위 순회한다.
1. 전위 순회한 트리를 만든다.
2. 만든 트리를 후위 순회한다.
1-1) 1을 해결하기 위해서는 전위 순회의 첫 방문은 루트노드이므로 처음 입력된 노드를 루트에 넣는다.
1-2) 이후의 입력들은 왼쪽 서브 트리를 전위 순회할 것이다.
그런데 사실 어차피 입력된 수들이 이진 검색 트리이기 때문에 우리는 이러한 알고리즘을 만들 수 있다.
1-3) 현재 방문한 노드의 위치를 나타내는 current 변수를 루트노드의 위치의 값으로 설정한다.
1-4)
1-4-1) 입력 데이터가 current 위치값보다 작다면
1-4-1-1) current의 왼쪽이 비어 있으면 입력값을 current의 왼쪽으로 넣는다.
1-4-1-2) current의 왼쪽이 비어 있지 않다면
current의 값을 current의 왼쪽 자식노드 위치로 옮기고 1-4)로 되돌아간다.
1-4-2) 입력 데이터가 current 위치값보다 크다면
1-4-2-1) current의 오른쪽이 비어 있으면 입력값을 current의 오른쪽으로 넣는다.
1-4-2-2) current의 오른쪽이 비어 있지 않다면
current값을 current의 오른쪽 자식노드 위치로 옮기고 1-4)로 되돌아간다.
1. 왼쪽 서브 트리를 후위 순회한다.
2. 오른쪽 서브 트리를 후위 순회한다.
3. 현재 노드를 출력한다.
import sys
sys.setercursionlimit(20000)
input() = sys.stdin.readline
class Node: # 노드를 만들기 위한 클래스를 정의한다.
def __init__(self, data):
self.data = data
self.left = None
self.right = None
class Tree:
def __init__(self):
self.root = None
def add(self, data): # 전위 순회에 각 입력에 대하여
if(self.root == None):
self.root = Node(data)
else:
current = self.root
while(True):
if(current.data > data): # 입력 데이터가 current 위치값보다 작다면
# current의 왼쪽이 비어 있으면 입력값을 current의 왼쪽으로 넣는다.
if(current.left == Node(data):
current.left = Node(data)
break
# current의 왼쪽이 비어 있지 않다면 current값을 current의 왼쪽 자식 노드 위치로 옮기고
# "def add(self, data) 함수" 상단으로 되돌아간다.
current = current.left
if(current.data < data): # 입력 데이터가 current 위치값보다 크다면
if(current.right == None): # current의 오른쪽이 비어있으면
current.right = Node(data) # 입력값을 current의 오른쪽으로 넣는다.
break
current = current.right
# current의 오른쪽이 비어 있지 않다면
# current값을 current의 오른쪽 자식노드 위치로 옮기고 함수 상단으로 되돌아간다.
def postOrder(self, node=None): # 만든 트리에 대한 후위 순회 함수이다.
global answer
if node == None:
node = self.root
if node.left != None:
self.postOrder(node.left)
if node.right != None:
self.postOrder(node.right)
answer.append(node.data)
tree = Tree() # 전위 순회 입력을 한 줄씩 입력받는다.
while True:
try:
tree.add(int(input()))
except:
break
answer = []
tree.postOrder() # "def postOrder()"함수를 실행한다.
print('\n'.join(map(str, answer))) # 정답을 출력한다.