트리는 그래프의 일종으로 정점과 간선을 이용하여 데이터의 배치 형태를 추상화한 자료구조이다.
간단한 예시로는 컴퓨터의 디렉토리가 있으며, 실제 나무를 거꾸로 한 것 같은 모양을 하고 있어 트리라고 부른다.
출처: https://yoongrammer.tistory.com/68
트리의 탐색 방법은 노드를 탐색하는 순서에 따라 전위 순회, 중위 순회, 후위 순회 3가지가 있다.
전위 순회의 탐색 순서는 다음과 같다.(F→B→A→D→C→E→G→I→H)
탐색 방법(A→b→C→D→E→G→H→I)
탐색 방법(A→C→E→D→B→H→I→G→F)
배열을 사용하여 구현할 때는 인덱스만 알면 노드의 부모자 자식을 쉽게 알 수 있다.
즉 배열로 트리를 구현할 때는 배열이라는 자료구조를 활용하여 트리를 개념적으로 생각하면 된다.
연결 리스트로 트리를 구현할 때는 양방향 연결 리스트를 활용해 주면 된다.
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct TreeNode {
int data;
struct TreeNode* left, * right;
} TreeNode;
// 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;
void inorder(TreeNode* root) {
if (root != NULL) {
inorder(root->left); // 왼쪽 자식 노드 순회
printf("[%d] ", root->data); // 노드 방문 및 출력
inorder(root->right); // 오른쪽 자식 노드 순회
}
}
void preorder(TreeNode* root) {
if (root != NULL) {
printf("[%d ", root->data); // 노드 방문 및 출력
preorder(root->left); // 왼쪽 자식 노드 순회
preorder(root->right); // 오른쪽 자식 노드 순회
}
}
void postorder(TreeNode* root) {
if (root != NULL) {
postorder(root->left); // 왼쪽 자식 노드 순회
postorder(root->right); // 오른쪽 자식 노드 순회
printf("[%d] ", root->data); // 노드 방문 및 출력
}
}
int main() {
printf("중위 순회: ");
inorder(root);
printf("\n");
printf("전위 순회: ");
preorder(root);
printf("\n");
printf("후위 순회: ");
postorder(root);
printf("\n");
return 0;
}
// 출력
// 중위 순회=[1] [4] [15] [16] [20] [25]
// 전위 순회=[15] [4] [1] [20] [16] [25]
// 후위 순회=[1] [4] [16] [25] [20] [15]
이진 트리는 여러개의 이진 트리가 하나로 연결되어 있는 것과 같기 때문에 재귀 호출을 이용하여 탐색할 수 있다.
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)
{
while (1) {
for (; root; root = root->left)
push(root);
root = pop();
if (!root) break;
printf("[%d] ", root->data);
root = root->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("중위 순회=");
inorder_iter(root);
printf("\n");
return 0;
}
// 출력
// 중위 순회=[1] [4] [15] [16] [20] [25]
탐색 순서는 다음과 같다.
#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];
}
먼저 원형 큐 구조체를 선언해주고 관련된 함수들을 구현해준다.
여기서 MAX_QUEUE_SIZE로 나눠주는 이유를 간략하게 설명해 보면
원형 큐가 포화 상태일 때 front가 0이고, rear이 7을 가리킨다. 이 때 그냥 +1을 해주면 존재하지 않는 인덱스인 8이 되는데 이를 최대 크기의 나머지를 구하면 인덱스 0에 접근할 수 있게 된다.
아래는 원형 큐를 활용한 레벨 순회 코드이다.
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);
}
}
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() {
printf("레벨 순회: ");
level_order(root);
printf("\n");
return 0;
}
// 출력
// 레벨 순회= [15] [4] [20] [1] [16] [25]
출처
C언어로 쉽게 풀어쓴 자료구조 - 천인구
https://yoongrammer.tistory.com/68
https://monsieursongsong.tistory.com/6
https://velog.io/@kimdukbae/자료구조-트리-Tree
https://gmlwjd9405.github.io/2018/08/12/data-structure-tree.html