트리
2021. 6. 18. 21:32ㆍ자료구조
728x90
트리
정의
- 계층적인 구조를 나타내는 자료구조
- 비선형 자료구조 (스트 , 스택, 큐 는 선형 구조임.)
- 트리는 부모 - 자식 관계의 노드들로 이루어짐.
- 1개 이상의 노드를 갖는 집합으로 노드들은 아래 조건을 만족함.
- root 라고 불리는 특별한 노드가 있음
- 다른 노드들은 원소가 중복되지 않는 n 개의 부속 트리 (subtree)임.
저장법
n-링크 표현법
- 노드에 n개의 링크를 두고 자식의 개수만큼 링크에 저장한다. 모든 노드는 자식 노드 수에 관계없이 최대 n개의 링크를 갖는다. 각 링크는 부속 트리가 저장된 곳을 링크한다.
왼쪽자식노드 - 오른쪽 형제노드 표현방법
(출처 : https://apape1225.tistory.com/57)
- 첫번째 링크는 첫번째 자식 노드를 표현 하고, 두번째 링크는 자신의 오른쪽 형제 노드를 표현한다,
용어
- 정점 (노드) : 트리의 구성 요소
- 루트 : 부모가 없는 노드
- 서브 트리 : 하나의 노드와 그 노드들의 자손들로 이루어진 트리
- 단말 노드 (terminal node) : 자식이 없는 노드
- 비 단말 노드 : 적어도 하나의 자식을 가지는 노드
- 차수 : 루트 노드가 가지고 있는 자식 노드의 개수
- 진출 차수 : 방향그래프에서 사용되는 용어로, 한 노드에서 외부로 향하는 간선의 수
- 진입 차수 : 방향그래프에서 사용되는 용어로 외부 노드에서 들어온 간선의 수
종류
이진 트리
정의
- 모든 노드가 2개의 서브 트리를 가지고 있는 트리 (서브 트리는 공집합일 수 있음.)
- 이진트리의 노드는 최대 2개까지 자식 노드가 존재
- 모든 노드의 차수는 2 이하가 됨 $$\Rightarrow$$ 구현하기 편리함
- 이진 트리에는 서브트리간의 순서가 존재함.
- 노드가 $$n$$개면 간선은 $$n-1$$개이다.
- 높이가 $$h$$인 이진트리의 경우, 최소 $$h$$개의 노드를 가지며 최대 $$2^h - 1$$개의 노드를 가진다.
- $$n$$개의 노드를 가지는 이진 트리의 높이는 최대 $$n$$, 최소 $$log_2(n-1)$$이다.
분류
- 포화 이진트리
- 용어 그대로 트리의 각 레벨에 노드가 꽉차 있는 이진 트리
- 전체 노드의 개수 : $$2^k - 1$$
- 완전 이진트리
- 레벨 1 부터 $$k-1$$까지는 노드가 모두 채워져있고, 마지막 레벨 $$k$$에서는 왼쪽부터 오른쪽으로 노드가 순서대로 채워져있는 이진트리
- 기타 이진트리
구현 방식
배열 표현법
- 모든 이진트리를 포화 이진트리라고 가정하고 각 노드에 번호를 붙여서 그 번호를 배열의 인덱스로 삼아 노드의 데이터를 배열에 저장하는 방법
단점
- skewed 이진트리처럼 깊이에 비해 노드 수가 적은 경우 기억 공간 활용률이 낮다.
- 트리의 최대 깊이를 대비하여 많은 기억 장소를 확보해야 하고, 트리가 예상보다 커지면 프로그램 수행을 종료해야 한다.
부모와 자식 인덱스 관계
- 노드 i의 부모 노드 인덱스 : i / 2
- 노드 i의 왼쪽 자식 노드 인덱스 : 2i
- 노드 i의 오른쪽 자식 노드 인덱스 : 2i + 1
링크 표현법
- 포인터를 이용하여 부모노드가 자식노드를 가리키게 하는 방법
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
typedef struct TreeNode {
int data;
struct TreeNode *left, *right;
} TreeNode;
// n1
// / |
// n2 n3
int main(void)
{
TreeNode *n1, *n2, *n3;
n1 = (TreeNode *)malloc(sizeof(TreeNode));
n2 = (TreeNode *)malloc(sizeof(TreeNode));
n3 = (TreeNode *)malloc(sizeof(TreeNode));
n1->data = 10; // 첫 번째 노드를 설정한다.
n1->left = n2;
n1->right = n3;
n2->data = 20; // 두 번째 노드를 설정한다.
n2->left = NULL;
n2->right = NULL;
n3->data = 30; // 세 번째 노드를 설정한다.
n3->left = NULL;
n3->right = NULL;
free(n1); free(n2); free(n3);
return 0;
}
이진 트리의 순회
전위 순회
- 자손 노드보다 루트 노드를 먼저 방문
void preorder(TreeNode *root) { if (root != NULL) { printf("[%d] ", root->data); // 노드 방문 preorder(root->left);// 왼쪽서브트리 순회 preorder(root->right);// 오른쪽서브트리 순회 } }
중위 순회
- 왼쪽 자손, 루트, 오른쪽 자손 순으로 방문
void inorder(TreeNode *root) { if (root != NULL) { inorder(root->left);// 왼쪽서브트리 순회 printf("[%d] ", root->data); // 노드 방문 inorder(root->right);// 오른쪽서브트리 순회 } }
후위 순회
- 루트노드보다 자손을 먼저 방문
- 왼쪽 서브트리 방문 하고, 오른쪽 서브트리 방문 후 루트 노드를 방문
void postorder(TreeNode *root) { if (root != NULL) { postorder(root->left);// 왼쪽서브트리 순회 postorder(root->right);// 오른쪽서브트리순회 printf("[%d] ", root->data); // 노드 방문 } }
반복 순회
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
typedef struct TreeNode {
int data;
struct TreeNode *left, *right;
} TreeNode;
#define SIZE 100
int top = -1;
TreeNode *stack[SIZE];
void push(TreeNode *p)
{
if (top < SIZE - 1)
stack[++top] = p;
}
TreeNode *pop()
{
TreeNode *p = NULL;
if (top >= 0)
p = stack[top--];
return p;
}
void inorder_iter(TreeNode *root) /* inoder () */
{
while (1) {
for (; root; root = root->left)
push(root);
root = pop();
if (!root) break;
printf("[%d] ", root->data);
root = root->right;
}
}
void preorder_iter(TreeNode *root)
{
push(root);
while(1){
root = pop();
if(!root) break;
printf("[%d] ", root -> data);
push(root -> right);
push(root -> left);
}
}
void postorder_iter(TreeNode *root)
{
/* source implementation? */
}
// 15
// 4 20
// 1 16 25
TreeNode n1 = { 1, NULL, NULL };
TreeNode n2 = { 4, &n1, NULL };
TreeNode n3 = { 16, NULL, NULL };
TreeNode n4 = { 25, NULL, NULL };
TreeNode n5 = { 20, &n3, &n4 };
TreeNode n6 = { 15, &n2, &n5 };
TreeNode *root = &n6;
int main(void)
{
printf("중위 순회=");
inorder_iter(root);
printf("\n");
printf("선위 순회=");
preorder_iter(root);
printf("\n");
printf("후위 순회=");
postorder_iter(root);
printf("\n");
return 0;
}
레벨 순회
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
typedef struct TreeNode {
int data;
struct TreeNode *left, *right;
} TreeNode;
// ================ 원형큐 코드 시작 =================
#define MAX_QUEUE_SIZE 100
typedef TreeNode * element;
typedef struct { // 큐 타입
element data[MAX_QUEUE_SIZE];
int front, rear;
} QueueType;
// 오류 함수
void error(char *message)
{
fprintf(stderr, "%s\n", message);
exit(1);
}
// 공백 상태 검출 함수
void init_queue(QueueType *q)
{
q->front = q->rear = 0;
}
// 공백 상태 검출 함수
int is_empty(QueueType *q)
{
return (q->front == q->rear);
}
// 포화 상태 검출 함수
int is_full(QueueType *q)
{
return ((q->rear + 1) % MAX_QUEUE_SIZE == q->front);
}
// 삽입 함수
void enqueue(QueueType *q, element item)
{
if (is_full(q))
error("큐가 포화상태입니다");
q->rear = (q->rear + 1) % MAX_QUEUE_SIZE;
q->data[q->rear] = item;
}
// 삭제 함수
element dequeue(QueueType *q)
{
if (is_empty(q))
error("큐가 공백상태입니다");
q->front = (q->front + 1) % MAX_QUEUE_SIZE;
return q->data[q->front];
}
void level_order(TreeNode *ptr)
{
QueueType q;
init_queue(&q); // 큐 초기화
if (ptr == NULL) return;
enqueue(&q, ptr);
while (!is_empty(&q)) {
ptr = dequeue(&q);
printf(" [%d] ", ptr->data);
if (ptr->left)
enqueue(&q, ptr->left);
if (ptr->right)
enqueue(&q, ptr->right);
}
}
// 15
// 4 20
// 1 16 25
TreeNode n1 = { 1, NULL, NULL };
TreeNode n2 = { 4, &n1, NULL };
TreeNode n3 = { 16, NULL, NULL };
TreeNode n4 = { 25, NULL, NULL };
TreeNode n5 = { 20, &n3, &n4 };
TreeNode n6 = { 15, &n2, &n5 };
TreeNode *root = &n6;
int main(void)
{
printf("레벨 순회=");
level_order(root);
printf("\n");
return 0;
}
728x90
'자료구조' 카테고리의 다른 글
다익스트라 알고리즘[Python] (0) | 2021.07.06 |
---|---|
그래프 (0) | 2021.06.18 |
우선순위 큐 (0) | 2021.06.18 |
후위표기식 전환 (스택) 2 (0) | 2021.06.18 |
후위표기식 전환 (스택이용) 1 (0) | 2021.06.18 |