[Data Structure] 이진 트리 반복적 순회/레벨 순회
2021. 12. 13. 14:48ㆍMajor`/자료구조
반복적 순회 (중위 순회)
- 스택을 이용해서 구현
- 스택에 자식 노드들을 push하고 pop하면서 순회
- 인공지능에서 지능적 탐색을 할 때 사용
알고리즘
- root에서부터 root->left로 진행하면서 stack에 push ( root = root->left를 통해서 반복) -- (1)
- root->left가 NULL이 되면 stack에서 하나씩 pop
- pop한 노드를 방문해서 해당 노드에서 node->right로 이동
- node->right가 NULL이면, 그냥 stack에서 pop
- node->right가 NULL이 아니면 node->right로 이동해서 (1) 진행
- stack이 공백이 되면 순회 종료
void inorder_iter(treenode* root) { // LVR 순으로 순회 while (1) { for (; root; root = root->left) // root부터 최종 root->left까지 스택에 push push(root); root = pop(); // 스택의 top에는 최종 root->left의 data가 존재하니까 pop해서 순회 if (!root) break; printf("[%d] ", root->data); // pop한 root->data print root = root->right; // LVR 순이니까 root->right는 마지막으로 순회 } }
※ Example
#include <stdio.h> #include <stdlib.h> // 이진트리 반복적 순회(중위 순회) typedef int element; typedef struct treenode { element data; struct treenode* left; struct treenode* right; }treenode; #define MAX_STACK_SIZE 1000 int top = -1; treenode* stack[MAX_STACK_SIZE]; // 노드들 저장하는 스택 정의 treenode n1 = { 8, NULL, NULL }; treenode n2 = { 39, NULL, NULL }; treenode n3 = { 85, NULL, NULL }; treenode n4 = { 2, NULL, NULL }; treenode n5 = { 15, &n1, &n2 }; treenode n6 = { 21, &n3, &n4 }; treenode n7 = { 30, &n5, &n6 }; treenode* root = &n7; // 트리의 루트 노드(n7)를 가리키는 root 포인터 // 30 // 15 21 // 8 39 85 2 // 중위순회 : 8 15 39 30 85 21 2 void push(treenode* p) { if (top < MAX_STACK_SIZE - 1) stack[++top] = p; } treenode* pop() { treenode* p = NULL; if (top >= 0) p = stack[top--]; return p; } void inorder_iter(treenode* root) { // LVR 순으로 순회 while (1) { for (; root; root = root->left) // root부터 최종 root->left까지 스택에 push push(root); root = pop(); // 스택의 top에는 최종 root->left의 data가 존재하니까 pop해서 순회 if (!root) break; printf("[%d] ", root->data); // pop한 root->data print root = root->right; // LVR 순이니까 root->right는 마지막으로 순회 } } int main(void) { printf("반복적 순회 (중위 순회) : "); inorder_iter(root); printf("\n"); return 0; }

레벨 순회
- 각 노드들을 레벨 순으로 검사하는 순회 방법
- 동일 레벨의 경우 좌 → 우로 방문
- 원형큐를 사용하여 구현
알고리즘
- Tree의 root를 먼저 enqueue -- (1)
- dequeue를 통해 나온 node->data print -- (2)
- 해당 node의 node->left != NULL이면 node->left를 enqueue -- (3)
- 그리고 해당 node의 node->right != NULL이면, node->right를 enqueue -- (4)
- 해당 node의 left, right가 모두 NULL이면 (2) 과정으로
- (2) ~ (4) 과정을 queue가 공백이 될 때 까지 계속 반복
void level_trv(treenode* root) { queue q; init(&q); if (root == NULL) return; enqueue(&q, root); // 루트 노드를 enqueue하고, 순회 시작 while (!is_empty(&q)) { root = dequeue(&q); // dequeue를 통해서 루트 노드 select printf("[%d] ", root->data); // 루트 노드의 data print if (root->left != NULL) // 루트 노드의 left가 NULL이 아니면 enqueue(&q, root->left); // 루트 노드의 left를 enqueue if (root->right != NULL) // 루트 노드의 right가 NULL이 아니면 enqueue(&q, root->right); // 루트 노드의 right를 enqueue } }
※ Example
#include <stdio.h> #include <stdlib.h> #define MAX_QUEUE_SIZE 100 typedef struct treenode { int data; struct treenode* left; struct treenode* right; }treenode; treenode n1 = { 8, NULL, NULL }; treenode n2 = { 39, NULL, NULL }; treenode n3 = { 85, NULL, NULL }; treenode n4 = { 2, NULL, NULL }; treenode n5 = { 15, &n1, &n2 }; treenode n6 = { 21, &n3, &n4 }; treenode n7 = { 30, &n5, &n6 }; treenode* root = &n7; // 트리의 루트 노드(n7)를 가리키는 root 포인터 // 30 // 15 21 // 8 39 85 2 // 레벨 순회 : 30 15 21 8 39 85 2 // 원형큐로 구현 typedef treenode* element; typedef struct queue { int front, rear; element queue[MAX_QUEUE_SIZE]; }queue; void init(queue* q) { q->front = q->rear = 0; } int is_empty(queue* q) { return q->front == q->rear; } int is_full(queue* q) { return q->front == (q->rear + 1) % MAX_QUEUE_SIZE; } void enqueue(queue* q, element item) { if (is_full(q)) return; else { q->rear = (q->rear + 1) % MAX_QUEUE_SIZE; q->queue[q->rear] = item; } } element dequeue(queue*q) { if (is_empty(q)) return; else { q->front = (q->front + 1) % MAX_QUEUE_SIZE; return q->queue[q->front]; } } void level_trv(treenode* root) { queue q; init(&q); if (root == NULL) return; enqueue(&q, root); // 루트 노드를 enqueue하고, 순회 시작 while (!is_empty(&q)) { root = dequeue(&q); // dequeue를 통해서 루트 노드 select printf("[%d] ", root->data); // 루트 노드의 data print if (root->left != NULL) // 루트 노드의 left가 NULL이 아니면 enqueue(&q, root->left); // 루트 노드의 left를 enqueue if (root->right != NULL) // 루트 노드의 right가 NULL이 아니면 enqueue(&q, root->right); // 루트 노드의 right를 enqueue } } int main(void) { printf("레벨 순회 : "); level_trv(root); printf("\n"); return 0; }
