Algorithm/DFS, BFS
2022. 4. 21. 01:46
문제인식)
다음과 같은 이진트리가 있다.
위 트리를 전위순회, 중위순회, 후위순회하려면 어떻게 해야할까?
참고로 전위순회, 중위순회, 후위순회에 대한 이름이 붙은 기준은
루트노드에 대한 작업순서를 기준으로 이름이 붙었다고 생각하면 쉬울 것이다.
즉,
루트노드를 먼저 작업한다면 전위순회 (루트노드 → 왼쪽서브트리 → 오른쪽서브트리),
루트노드를 중간에 작업한다면 중위순회 (왼쪽서브트리 → 루트노드 → 오른쪽서브트리),
루트노드를 마지막에 작업한다면 후위순회 (왼쪽서브트리 → 오른쪽서브트리 → 루트노드)
가 되는 것이다.
이렇게 각 순회에 따른 출력 결과는 다음과 같아야 할 것이다.
전위순회 | 1 2 4 5 3 6 7 |
중위순회 | 4 2 5 1 6 3 7 |
후위순회 | 4 5 2 6 7 3 1 |
왜 이렇게 나오는지 전위순회를 기준으로 설명을 하자면
루트노드 "1"에 대한 작업(출력)을 하고,
1 //현재까지의 출력
"1"의 왼쪽서브트리에 접근한다. ("1"의 오른쪽서브트리는 왼쪽서브트리의 작업이 끝난 후 진행)
접근한 왼쪽서브트리의 루트노드 "2"에 대한 작업(출력)을 하고,
1 2 //현재까지의 출력
"2"의 왼쪽서브트리에 접근한다. ("2"의 오른쪽서브트리는 왼쪽서브트리의 작업이 끝난 후 진행)
접근한 왼쪽서브트리의 루트노드 "4"에 대한 작업(출력)을 하고,
1 2 4 //현재까지의 출력
"4"의 왼쪽서브트리는 없으므로 "4"의 루트노드인 "2"로 회귀
"2"의 왼쪽서브트리에 대한 작업이 끝났으므로 "2"의 오른쪽서브트리에 접근한다.
접근한 왼쪽서브트리의 루트노드 "5"에 대한 작업(출력)을 하고,
1 2 4 5 //현재까지의 출력
.
.
.
이후 이런식으로 반복이 되는 것이다. (재귀가 생각나지 않은가?)
마찬가지로 중위순회, 후위순회에 따른 접근순서를 gif로 묘사하자면 다음과 같다.
이를 이제 재귀로 풀어볼 것이다.
해결과정)
사실 문제인식만 잘하여도 절반을 푼 것이나 마찬가지이다.
이제 나머지 절반을 완성시켜보자.
전위순회를 의사코드로 풀어보자면
void 함수(현재노드) {
기저조건을 설정한다.
현재노드에 대한 작업을 한다. //전위, preorder
함수(왼쪽 서브트리의 루트노드);
함수(오른쪽 서브트리의 루트노드);
}
가 된다. (마찬가지로 중위순회, 후위순회에 대한 의사코드도 쉽게 작성할 수 있을 것이다.)
이제 실제로 작동하는 코드(여기서는 C++)로 옮기기 위해 몇가지 설정을 하자면...
1) 트리의 루트노드이자 시작점은 "1"로 설정한다.
2) 기저조건은 7을 넘어가면 종료하도록 설정한다.
3) 작업은 단순히 현재노드의 번호를 출력하는 것으로 설정한다.
4) 왼쪽(루트)노드는 본 노드의 번호 × 2로 접근한다.
5) 오른쪽(루트)노드는 본 노드의 번호 × 2 + 1로 접근한다.
이제 전위 순회를 실제 코드로 작성해보자.
#include<cstdio>
void preOrder(int n) {
if(n>7) return; //기저조건
printf("%d ", n); //전위
preOrder(n*2); //왼쪽 서브트리의 루트노드
preOrder(n*2+1); //오른쪽 서브트리의 루트노드
}
int main() {
preOrder(1); // 1에서부터 트리탐색 시작
return 0;
}
나머지 중위순회, 후위순회도
작업(출력)과 접근의 순서만 바꾸면
완성이다. (여기서는 함수만 작성, 메인함수에서는 똑같이 1부터 시작하도록 하면 된다.)
void inOrder(int n) {
if(n>7) return; //기저조건
inOrder(n*2); //왼쪽 서브트리의 루트노드
printf("%d ", n);//중위
inOrder(n*2+1); //오른쪽 서브트리의 루트노드
}
void postOrder(int n) {
if(n>7) return; //기저조건
postOrder(n*2); //왼쪽 서브트리의 루트노드
postOrder(n*2+1); //오른쪽 서브트리의 루트노드
printf("%d ", n); //후위
}
예시는 단순하게 했지만,
작업할 노드의 번호를 객체의 속성으로 대신하거나,
작업을 단순히 출력만 하는 것이 아닌, 여러 작업으로 대신한다면
수많고 놀라우리만큼 확장성있는 프로그래밍을 할 수 있을 것이다.