계층적으로 저장된 데이터 원소를 모델링
루트 원소는 0개의 부모, 0개 이상의 자식을 갖는다.
루트가 아닌 원소는 1개의 부모, 0개 이상의 자식을 갖는다.
Alg depth(v)
input node v
output depth of v
1. if(isRoot(v))
return 0
else
return 1 + depth(parent(v))
Alg height(v)
input node v
output height of v
1. if(isExternal(v))
return 0
else
h ← 0
for each c ∈ children(v)
h ← max(h, height(c))
reutrn 1 + h
A
┌──────┐
B C
┌─┬─┐ │
D E F G
위 트리에 대해서
Alg preorder()
1. visit(v)
2. for each child ∈ children(v)
preorder(child)
Alg postorder()
1. for each child ∈ children(v)
postorder(child)
2. visit(v)
Alg levelorder()
1. Q.enqueue(v)
2. while(!Q.isEmpty)
v ← Q.dequeue()
visit(v)
for each child ∈ children(v)
Q.enqueue(child)
3. return
*** 방안2를 택할 경우 children(v) 처리시간은 자식개수만큼 소요
트리ADT의 확장
Alg preorder(v)
1. visit(v)
2. if(isInternal(v))
preorder(leftChild(v))
preorder(rightChild(v))
Alg inorder()
1. if(isInternal(v))
inorder(leftChild(v))
2. visit(v)
3. if(isInternal(v))
inorder(rightChild(v))
Alg postorder()
1. if(isInternal(v))
postorder(leftChild(v))
postorder(rightChild(v))
2. visit(v)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct _Node {
struct _Node* left;
struct _Node* right;
int elem;
} Node;
Node* getNode() {
Node* p = (Node*)malloc(sizeof(Node));
p->left = NULL;
p->right = NULL;
return p;
}
Node* findNodeByElem(Node* p, int elem) {
if (p->elem == elem)
return p;
if (p->left != NULL) {
Node* left = findNodeByElem(p->left, elem);
if (left != NULL)
return left;
}
if (p->right != NULL) {
Node* right = findNodeByElem(p->right, elem);
if (right != NULL)
return right;
}
return NULL;
}
void addLeftNode(Node* root, int parentElem, int elem) {
Node* newNode = getNode();
newNode->elem = elem;
Node* parent = findNodeByElem(root, parentElem);
parent->left = newNode;
}
void addRightNode(Node* root, int parentElem, int elem) {
Node* newNode = getNode();
newNode->elem = elem;
Node* parent = findNodeByElem(root, parentElem);
parent->right = newNode;
}
void preOrder(Node* p) {
printf("%d ", p->elem);
if (p->left != NULL)
preOrder(p->left);
if (p->right != NULL)
preOrder(p->right);
}
void inOrder(Node* p) {
if (p->left != NULL)
inOrder(p->left);
printf("%d ", p->elem);
if (p->right != NULL)
inOrder(p->right);
}
void postOrder(Node* p) {
if (p->left != NULL)
postOrder(p->left);
if (p->right != NULL)
postOrder(p->right);
printf("%d ", p->elem);
}
int main() {
Node* root = getNode();
root->elem = 1;
addLeftNode(root, 1, 2);
addLeftNode(root, 2, 3);
addRightNode(root, 2, 4);
addRightNode(root, 1, 5);
addLeftNode(root, 5, 6);
addRightNode(root, 5, 7);
addLeftNode(root, 6, 8);
addRightNode(root, 6, 9);
addRightNode(root, 9, 10);
/*
1
2 5
3 4 6 7
8 9
10
*/
preOrder(root); printf("\n"); // 1 2 3 4 5 6 8 9 10 7
inOrder(root); printf("\n"); // 3 2 4 1 8 6 9 10 5 7
postOrder(root); printf("\n"); // 3 4 2 8 10 9 6 7 5 1
return 0;
}
루트에서 왼쪽 자식노드 방향으로 출발. 트리의 간선을 항상 왼쪽에 두고 트리를 돌며 순회.
각 노드별로 세 번씩 방문
Alg eulerTour(v)
1. visitLeft(v)
2. if(isInternal(v))
eulerTour(leftChild(v))
3. visitBelow(v)
4. if(isInternal(v))
eulerTour(rightChild(v))
5. visitRight(v)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct _Node {
struct _Node* left;
struct _Node* right;
int elem;
} Node;
Node* getNode() {
Node* p = (Node*)malloc(sizeof(Node));
p->left = NULL;
p->right = NULL;
return p;
}
Node* findNodeByElem(Node* p, int elem) {
if (p->elem == elem)
return p;
if (p->left != NULL) {
Node* left = findNodeByElem(p->left, elem);
if (left != NULL)
return left;
}
if (p->right != NULL) {
Node* right = findNodeByElem(p->right, elem);
if (right != NULL)
return right;
}
return NULL;
}
void addLeftNode(Node* root, int parentElem, int elem) {
Node* newNode = getNode();
newNode->elem = elem;
Node* parent = findNodeByElem(root, parentElem);
parent->left = newNode;
}
void addRightNode(Node* root, int parentElem, int elem) {
Node* newNode = getNode();
newNode->elem = elem;
Node* parent = findNodeByElem(root, parentElem);
parent->right = newNode;
}
void preOrder(Node* p) {
printf("%d ", p->elem);
if (p->left != NULL)
preOrder(p->left);
if (p->right != NULL)
preOrder(p->right);
}
void inOrder(Node* p) {
if (p->left != NULL)
inOrder(p->left);
printf("%d ", p->elem);
if (p->right != NULL)
inOrder(p->right);
}
void postOrder(Node* p) {
if (p->left != NULL)
postOrder(p->left);
if (p->right != NULL)
postOrder(p->right);
printf("%d ", p->elem);
}
void eulerTour(Node* p) {
// visit left
printf("%d(L) ", p->elem);
if (p->left != NULL) // ( is internal )
eulerTour(p->left);
// visit below
printf("%d(B) ", p->elem);
if (p->right != NULL) // ( is internal )
eulerTour(p->right);
// visit right
printf("%d(R) ", p->elem);
}
int main() {
Node* root = getNode();
root->elem = 1;
addLeftNode(root, 1, 2);
addLeftNode(root, 2, 3);
addRightNode(root, 2, 4);
addRightNode(root, 1, 5);
addLeftNode(root, 5, 6);
addRightNode(root, 5, 7);
addLeftNode(root, 6, 8);
addRightNode(root, 6, 9);
addRightNode(root, 9, 10);
/*
1
2 5
3 4 6 7
8 9
10
*/
eulerTour(root);
// 1(L) 2(L) 3(L) 3(B) 3(R) 2(B) 4(L) 4(B) 4(R) 2(R) 1(B) 5(L) 6(L) 8(L) 8(B) 8(R)
// 6(B) 9(L) 9(B) 10(L) 10(B) 10(R) 9(R) 6(R) 5(B) 7(L) 7(B) 7(R) 5(R) 1(R)
return 0;
}
오일러 투어를 통해 어떤 노드 v의 부트리의 크기를 구할 수 있다.
카운터 변수 cnt를 두고 진행한다.
rightCnt - leftCnt + 1
int cnt = 0;
void eulerTour(Node* p) {
// visit left
cnt++;
p->leftCnt = cnt;
if (p->left != NULL) // ( is internal )
eulerTour(p->left);
// visit below
// (no action)
if (p->right != NULL) // ( is internal )
eulerTour(p->right);
// visit right
printf("%d's subtree size : %d\n", p->elem, cnt - (p->leftCnt) + 1);
}
int main() {
Node* root = getNode();
root->elem = 1;
addLeftNode(root, 1, 2);
addLeftNode(root, 2, 3);
addRightNode(root, 2, 4);
addRightNode(root, 1, 5);
addLeftNode(root, 5, 6);
addRightNode(root, 5, 7);
addLeftNode(root, 6, 8);
addRightNode(root, 6, 9);
addRightNode(root, 9, 10);
/*
1
2 5
3 4 6 7
8 9
10
*/
eulerTour(root);
/*
3's subtree size : 1
4's subtree size : 1
2's subtree size : 3
8's subtree size : 1
10's subtree size : 1
9's subtree size : 2
6's subtree size : 4
7's subtree size : 1
5's subtree size : 6
1's subtree size : 10
*/
return 0;
}
n개 노드가 있는 크기 N의 일차원배열로 이진트리를 구현한다.
랭크(인덱스) i>0인 노드에 관해
효율적인 공간사용에 대해
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#define N 20
int tree[N] = { 0 };
void addNode(int parent, int leftElem, int rightElem) {
// parent인덱스 찾기
int parentIdx = 0;
for (int i = 0; i < N; i++) {
if (tree[i] == parent) {
parentIdx = i;
break;
}
}
// 좌우 자식노드 추가
tree[2 * parentIdx] = leftElem;
tree[2 * parentIdx + 1] = rightElem;
}
void printTree() {
for (int i = 0; i < N; i++) {
printf("%d ", tree[i]);
}
printf("\n");
}
void inorderVisit(int idx) {
if (tree[2 * idx] != 0)
inorderVisit(2 * idx);
printf("%d ", tree[idx]);
if (tree[2 * idx + 1] != 0)
inorderVisit(2 * idx + 1);
}
int main() {
tree[1] = 1; // root
addNode(1, 2, 5);
addNode(2, 3, 4);
addNode(5, 6, 7);
addNode(6, 8, 9);
/*
1
2 5
3 4 6 7
8 9
*/
printTree();
// 0 1 2 5 3 4 6 7 0 0 0 0 8 9 0 0 0 0 0 0
inorderVisit(1);
// 3 2 4 1 8 6 9 5 7
return 0;
}
노드 저장내용
아래 코드는 내부노드가 좌우자식을 모두 가진다고 가정한다.
Alg preorderSucc()
1. if(isInternal(v))
return leftChild(v)
2. p ← parent(v)
3. while(leftChild(p) != v)
if(isRoot(v)) {이 조건에 걸리려면, v가 마지막 순회순서의 노드여야한다.}
invalidNodeException()
v ← p
p ← parent(p)
4. return rightChild(p)
Alg inorderSucc()
1. if(isInternal(v)) { 오른쪽 부트리의 왼쪽끝 노드 반환 }
v ← rightChild()
while(isInternal(v))
v ← leftChild()
return v
2. p ← parent
3. while(leftChild(p) != v) { 다음에 출력할 내부노드 찾기 }
if(isRoot(v))
invalidNodeException()
v ← p
p ← parent(p)
4. return p
Alg postorderSucc()
1. if(isRoot(v))
invalidNodeException() { 후위순회에서 루트 다음 방문할 노드는 없다 }
2. p ← parent(p)
3. if(rightChild(p) = v)
return p
4. v ← rightChild(p)
5. while(isInternal(v)) { 오른쪽 부트리의 왼쪽끝 노드 반환 }
v ← leftChild(v)
6. return v
Node* preOrderSuccessor(Node* root, Node* v) {
// 내부노드면 그냥 left 반환
if (v->left != NULL || v->right != NULL)
return v->left;
// 1. 이 노드가 부모 노드의 left였으면 그냥 부모의 right 반환
// 2. 이 노드가 부모 노드의 right였으면, 아직 출력안한 가장 가까운 right반환
Node* p = v->parent;
while (p->left != v) {
if (v == root)
return NULL; // invalid exception
v = p;
p = p->parent;
}
return p->right;
}
Node* inOrderSuccessor(Node* root, Node* v) {
// 내부노드면 오른쪽 부트리의 왼쪽 끝 노드 반환
if (v->left != NULL && v->right != NULL) {
v = v->right;
while (v->left != NULL && v->right != NULL)
v = v->left;
return v;
}
// 외부노드일 때
// 1. 이 노드가 부모노드의 left였으면, 부모노드 반환
// 2. 이 노드가 부모노드의 right였으면, 아직 출력안한 가장 가까운 right반환
Node* p = v->parent;
while (p->left != v) {
if (v == root)
return NULL; // invalid exception
v = p;
p = p->parent;
}
return p;
}
Node* postOrderSuccessor(Node* root, Node* v) {
if (v == root)
return NULL; // invalid exception
Node* p = v->parent;
// 1. 이 노드가 외부노드이고 + 부모노드의 right였으면 ---> 부모노드 반환
// 2. 이 노드가 내부노드이고 + 부모노드의 right였으면 ---> 부모노드 반환
if (p->right == v)
return p;
// 2-1. 이 노드가 외부노드이고 + 부모노드의 left였으면 ---> 부모노드 오른쪽 부트리의 왼쪽 끝 노드 반환
// 2-2. 이 노드가 내부노드이면 + 부모노드의 left였으면 ---> 부모노드 오른쪽 부트리의 왼쪽 끝 노드 반환
v = p->right;
while (v->left != NULL && v->right != NULL)
v = v->left;
return v;
}
int main() {
Node* root = getNode();
root->elem = 1;
addLeftNode(root, 1, 2);
addLeftNode(root, 2, 3);
addRightNode(root, 2, 4);
addRightNode(root, 1, 5);
addLeftNode(root, 5, 6);
addRightNode(root, 5, 7);
addLeftNode(root, 4, 8);
addRightNode(root, 4, 9);
/*
1
2 5
3 4 6 7
8 9
*/
Node* v1 = findNodeByElem(root, 2);
Node* v2 = findNodeByElem(root, 4);
Node* v3 = findNodeByElem(root, 6);
printf("\t\tv1(%d)\tv2(%d)\tv3(%d)\n", v1->elem, v2->elem, v3->elem);
printf("pre suc\t\t%d\t%d\t%d\n", preOrderSuccessor(root, v1)->elem, preOrderSuccessor(root, v2)->elem, preOrderSuccessor(root, v3)->elem);
printf("in suc\t\t%d\t%d\t%d\n", inOrderSuccessor(root, v1)->elem, inOrderSuccessor(root, v2)->elem, inOrderSuccessor(root, v3)->elem);
printf("post suc\t%d\t%d\t%d\n", postOrderSuccessor(root, v1)->elem, postOrderSuccessor(root, v2)->elem, postOrderSuccessor(root, v3)->elem);
/*
v1(2) v2(4) v3(6)
pre suc 3 8 7
in suc 8 9 5
post suc 6 2 7
*/
return 0;
}
의사결정 과정을 묘사한 이진트리