Data Structure & Algorithm
[자료구조] 이진트리 표현법 (in python)
여뉴누
2023. 6. 18. 21:55
728x90
강의출처) 숙명여자대학교 소프트웨어융합전공 강의 "자료구조", 이현자 교수님
코드 결과)
https://github.com/nyunu/data_structure/blob/main/%EC%9D%B4%EC%A7%84%ED%8A%B8%EB%A6%AC.ipynb
GitHub - nyunu/data_structure
Contribute to nyunu/data_structure development by creating an account on GitHub.
github.com
이진트리 표현법
1. 선형리스트 이용 [배열 표현법]
- 루트를 배열의 1번 인덱스에 저장
-> 나머지 노드들도 위치에 따라 번호 부여
-> 배열에 각 노드를 인덱스 번호에 맞게 저장 - 0번 인덱스는 인덱스로 트리의 관계를 나타낼 수 있기 때문에 비워둠
- 완전 이진 트리일 때 유리
-> 한쪽으로 치우친 이진트리이면 공간 낭비 O - i번째 노드에 대해
-> 부모 노드 인덱스 =
-> 왼쪽 자식 노드 인덱스 =
-> 오른쪽 자식 노드 인덱스 =
2. 연결리스트 이용 [링크 표현법]
- 트리의 형태 상관없이 공간 낭비 X
- 노드 삽입, 삭제에 유리한 구조
- 노드를 포인터가 가리키도록
코드 구현
class Node:
def __init__(self, item):
self.data = item
self.rchild = None
self.lchild = None
이진트리 순회
트리순회란 ?
: 트리에 속하는 모든 노드를 한번씩 방문하는 것
트리순회 종류
: 루트 방문 순서에 따라 구분 (중간에, 처음에, 마지막에)
- 중위 순서 순회
- 후위 순서 순회
- 전위 순서 순회
- 레벨 순서 순회
1. 중위 순서 순회
: 왼쪽 서브트리 -> 루트 -> 오른쪽 서브트리 순서로 순회
1) 순회 과정 및 결과
<순회 결과>
<순회 과정>
- (파란색) 처음에는 루트 노드 기준으로 왼쪽 서브트리, 오른쪽 서브트리 구분
- (파란색) 그 중 왼쪽 서브트리부터 순회
- (초록색) 왼쪽 서브트리에서의 루트 노드를 기준으로 왼쪽 서브트리, 오른쪽 서브트리 구분
- (초록색) 동일하게 그 중 왼쪽 서브트리부터 순회
- (보라색) 보라색 서브트리를 보면 루트노드 + 자식노드로만 이루어져 있음. 즉, 자식노드 = 리프노드
-> 왼쪽 자식노드인 A 순회
-> 보라색 서브트리의 루트노드인 * 순회
-> 오른쪽 자식노드인 B 순회 - (초록색) 초록색 서브트리 기준으로 왼쪽 트리 순회가 끝난 것
-> 초록색 서브트리의 루트 노드인 + 순회
-> 초록색 서브트리의 오른쪽 자식노드인 C 순회 - 전체 트리 기준으로 왼쪽 서브트리의 순회가 끝난 것
-> 파란색 전체트리의 루트 노드인 - 순회 - (파란색) 오른쪽 서브트리 순회
- (초록색) 초록색 서브트리의 자식노드 = 리프노드
-> 초록색 서브트리의 왼쪽 자식노드인 D 순회
-> 초록색 서브트리의 루트노드인 / 순회
-> 초록색 서브트리의 오른쪽 자식노드인 E 순회
2) 코드 구현
: 위의 과정을 재귀함수로 표현
(1) 왼쪽 서브노드 방문
(2) 루트 노드 방문
(3) 오른쪽 노드 방문
def inorder(self, ptr):
# 최초의 ptr은 root노드로 주어짐
if ptr:
self.stack.append(ptr.data)
# 루트 노드의 데이터를 스택에 추가
print('', self.stack)
self.inorder(ptr.lchild)
# 루트 노드 기준 왼쪽 서브트리 순회
print(ptr.data, end = '')
self.stack.pop()
# 루트 노드 순회
print(self.stack)
self.inorder(ptr.rchild)
# 루트 노드 기준 오른쪽 서브트리 순회
2. 후위 순서 순회
: 왼쪽 서브트리 -> 오른쪽 서브트리 -> 루트 순서로 순회
1) 순회 결과
2) 코드 구현
: 재귀함수로 표현
(1) 왼쪽 서브노드 방문
(2) 오른쪽 노드 방문
(3) 루트 노드 방문
def postorder(self, ptr):
# 최초의 ptr은 root노드로 주어짐
if ptr:
self.stack.append(ptr.data)
# ptr 노드의 값을 stack에 저장
print('', self.stack)
self.postorder(ptr.lchild)
# ptr 노드 기준 왼쪽 서브트리 순회
self.postorder(ptr.rchild)
# ptr 노드 기준 오른쪽 서브트리 순회
print(ptr.data, end = '')
self.stack.pop()
# 왼쪽, 오른쪽 모두 순회한 서브트리의 루트 노드를 pop
print(self.stack)
3) 코드 과정
- (파란색) ( - ) ptr로 입력되며 코드 시작
- (초록색) 루트 노드의 왼쪽 서브트리 기준으로 함수 호출 -> (+)가 루트 노드
- (보라색) 초록색 서브트리의 왼쪽 서브트리 기준으로 함수 호출 -> (*)가 루트 노드
- ptr이 (*) 노드인 상황에서 ptr.lchild 호출
-> A가 ptr이 되고
-> A를 스택에 추가
-> ptr(A).lchild 호출
-> if ptr == False 가 되어 그대로 return
-> ptr(A).rchild 호출
-> if ptr == False 가 되어 그대로 return
-> 스택의 가장 마지막 값인 A를 pop
=> 이 과정의 반복
3. 전위 순서 순회
: 루트 -> 왼쪽 서브트리 -> 오른쪽 서브트리 순서로 순회
1) 순회 결과
2) 코드 구현
: 재귀함수로 표현
(1) 루트 노드 방문
(2) 왼쪽 서브노드 방문
(3) 오른쪽 노드 방문
def preorder(self, ptr):
if ptr:
self.stack.append(ptr.data)
print('', self.stack)
print(ptr.data, end = '')
self.stack.pop()
# 지금 함수가 가리키는 ptr의 데이터 값 pop
print(self.stack)
self.preorder(ptr.lchild)
# ptr 노드 기준 왼쪽 서브트리 순회
self.preorder(ptr.rchild)
# ptr 노드 기준 오른쪽 서브트리 순회
3) 코드 과정
- (파란색) ( - ) ptr로 입력되며 코드 시작 -> 루트 pop
- (초록색) 루트 노드의 왼쪽 서브트리 기준으로 함수 호출 -> (+)가 루트 노드 -> pop
- (보라색) 초록색 서브트리의 왼쪽 서브트리 기준으로 함수 호출 -> (*)가 루트 노드 -> pop
- ptr이 (*) 노드인 상황에서 ptr.lchild 호출
-> A가 ptr이 되고
-> A를 스택에 추가
-> A를 pop
-> ptr(A).lchild 호출
-> if ptr == False 가 되어 그대로 return
-> ptr(A).rchild 호출
-> if ptr == False 가 되어 그대로 return
=> 위 과정의 반복
4. 레벨 순서 순회
: 루트 노드 -> 리프 노드 (왼 -> 오) 순서로 순회
1) 순회 결과
2) 코드 구현
: queue를 사용하여 구현 (cf. 앞선 전위, 중위, 후위 방식은 stack 사용)
def levelorder(self, node):
if not node : return
# root가 None이면 return
self.queue.append(node)
# queue에 node(root 노드)를 추가
while len(self.queue) > 0:
# queue에 원소가 있는 동안
node = self.queue.pop(0)
# queue의 첫번째 원소를 pop
print(node.data, end = '')
# 위에서 pop한 데이터를 출력
if node.lchild:
# 만약 node의 lchild가 존재하면
self.queue.append(node.lchild)
# node의 lchild를 queue에 추가
if node.rchild:
# 만약 node의 rchild가 존재하면
self.queue.append(node.rchild)
# node의 rchild를 queue에 추가
3) 코드 과정
- root 노드로 코드 시작
- queue에 root 노드 값 먼저 넣어준 뒤
- queue에 원소가 있는 동안 while문 실행
- queue 원소 중 가장 먼저 추가된 노드 순회
- queue 원소 중 가장 먼저 추가된 노드의 lchild, rchild를 순서대로 queue에 저장
전체 코드
class Node:
def __init__(self, item):
self.data = item
self.rchild = None
self.lchild = None
class Tree:
def __init__(self):
self.queue = []
self.stack = []
self.output = ''
# 레벨 순서 순회
def levelorder(self, node):
if not node : return
# root가 None이면 return
self.queue.append(node)
# queue에 node(root 노드)를 추가
while len(self.queue) > 0:
# queue에 원소가 있는 동안
node = self.queue.pop(0)
# queue의 첫번째 원소를 pop
print(node.data, end = '')
# 위에서 pop한 데이터를 출력
if node.lchild:
# 만약 node의 lchild가 존재하면
self.queue.append(node.lchild)
# node의 lchild를 queue에 추가
if node.rchild:
# 만약 node의 rchild가 존재하면
self.queue.append(node.rchild)
# node의 rchild를 queue에 추가
# 후위 순서 순회
def postorder(self, ptr):
# 최초의 ptr은 root노드로 주어짐
if ptr:
self.stack.append(ptr.data)
print('', self.stack)
self.postorder(ptr.lchild)
# ptr 노드 기준 왼쪽 서브트리 순회
self.postorder(ptr.rchild)
# ptr 노드 기준 오른쪽 서브트리 순회
print(ptr.data, end = '')
self.stack.pop()
print(self.stack)
def inorder(self, ptr):
# 최초의 ptr은 root노드로 주어짐
if ptr:
self.stack.append(ptr.data)
# 루트 노드의 데이터를 스택에 추가
print('', self.stack)
self.inorder(ptr.lchild)
# 루트 노드 기준 왼쪽 서브트리 순회
print(ptr.data, end = '')
self.output += self.stack.pop()
# 루트 노드 순회
print(self.stack)
self.inorder(ptr.rchild)
# 루트 노드 기준 오른쪽 서브트리 순회
def inorder_view(self):
print(self.output)
def preorder(self, ptr):
if ptr:
self.stack.append(ptr.data)
print('', self.stack)
print(ptr.data, end = '')
self.stack.pop()
# 지금 함수가 가리키는 ptr의 데이터 값 pop
print(self.stack)
self.preorder(ptr.lchild)
# ptr 노드 기준 왼쪽 서브트리 순회
self.preorder(ptr.rchild)
# ptr 노드 기준 오른쪽 서브트리 순회
def viewQueue(self):
print('[', end = '')
for item in self.queue:
print(item.data, end = '')
print(']')
728x90