내돈내고 내가 공부한것을 올리며, 중요한 단원은 저 자신도 곱씹어 볼겸 가겹게 포스팅 하겠습니다.
1) 8장. 트리
대부분의 교제에서 가르치는 트리중 대표가 아마 이진트리 일 것 이다. 이진트리의 정의를 간략히 살펴보자!
1) 공집합이거나
2) 루트와 왼쪽 서브트리, 오른쪽 서브트리로 구성된 노드들의 유한 집합으로 정의된다. 이진트리의 서브트리들은 모두 이진트리여야 한다.
정의 8.1 299페이지
Binary Tree 의 ADT
데이터: 노드와 간선의 집합. 노드는 공집합이거나 공집합이 아니라면 루트노드와 양쪽 서브트리로 구성된다. 모든 서브트리는 이진트리여야 한다.
연산:
- create(): 이진트리를 생성한다.
- isEmpty(): 이진트리가 공백 상태인지 확인한다.
- getRoot(): 이진트리의 루트 노드를 반환.
- getCount(): 이진트리의 노드의 수를 반환.
- getHeight(): 이진트리의 높이를 반환
- insertNode(n): 이진트리에 노드 n을 삽입.
- deleteNode(n): 이진트리에서 노드 n을 삭제한다.
- display(): 이진트리의 내용을 화면에 출력
이진트리 노드 class구현, 교제와 다르게 template화 적용
BinaryNode.h
#pragma once
#include <cstdio>
template <typename T>
class BinaryNode {
protected:
T _data;
BinaryNode<T>* _left;
BinaryNode<T>* _right;
public:
BinaryNode(T val = 0, BinaryNode<T>* l = nullptr, BinaryNode<T>* r = nullptr)
: _data(val), _left(l), _right(r) {}
void setData(T val) { _data = val; }
void setLeft(BinaryNode<T>* l) { _left = l; }
void setRight(BinaryNode<T>* r) { _left = r; }
T getData() { return _data; }
BinaryNode<T>* getLeft() { return _left; }
BinaryNode<T>* getRight() { return _right; }
bool isLeaf() { return _left == nullptr && _right == nullptr; }
};
BinaryTree.h
#pragma once
#include "BinaryNode.h"
template <typename U>
class BinaryTree {
BinaryNode<U>* _root;
public:
BinaryTree(): _root(nullptr) {}
void setRoot(BinaryNode* node) { root = node; }
BinaryNode<U>* getRoot() { return _root; }
bool isEmpty() { return root == nullptr; }
void inorder(); // 아직 미구현
void preorder();
void postorder();
void levelorder();
int getCount();
int getHeight();
int getLeafCount();
};
이진트리 순회 방법
루트 방문을 V, 왼쪽 서브트리 방문을 L, 오른쪽 서브트리 방문을 R이라고 하면 다음과 같은 3가지 방법이 있다. 의사코드를 통하여 확인해보자.
- 전위 순회(preorder traveral): VLR
preorder(x)
if x ≠ NULL
then print DATA(X);
preorder(LEFT(x));
preorder(RIGHT(x));
- 중위 순회(inorder traveral): LVR
inorder(x)
if x ≠ NULL
then preorder(LEFT(x));
print DATA(x);
preorder(RIGHT(x));
- 후위 순회(postorder traversal): LRV
postorder(x)
if x ≠ NULL
then preorder(LEFT(x));
preorder(RIGHT(x));
print DATA(x);
이를 활용하여 위의 BinaryTree.h를 완성시켜보자.
#pragma once
#include "BinaryNode.h"
template <typename U>
class BinaryTree {
BinaryNode<U>* _root;
public:
BinaryTree(): _root(nullptr) {}
void setRoot(BinaryNode<U>* node) { _root = node; }
BinaryNode<U>* getRoot() { return _root; }
bool isEmpty() { return _root == nullptr; }
void inorder(BinaryNode<U>* node);
void inorder() { std::cout << "\ninorder: "; inorder(_root); }
void preorder(BinaryNode<U>* node);
void preorder() { std::cout << "\npreorder: "; preorder(_root); }
void postorder(BinaryNode<U>* node);
void postorder() { std::cout << "\npostorder: "; postorder(_root); }
void levelorder();
int getCount();
int getHeight();
int getLeafCount();
};
template <typename U>
void BinaryTree<U>::inorder(BinaryNode<U>* node)
{
if (node != nullptr) {
inorder(node->getLeft());
std::cout << node->getData() << " ";
inorder(node->getRight());
}
}
template <typename U>
void BinaryTree<U>::preorder(BinaryNode<U>* node)
{
if (node != nullptr) {
std::cout << node->getData() << " ";
preorder(node->getLeft());
preorder(node->getRight());
}
}
template <typename U>
void BinaryTree<U>::postorder(BinaryNode<U>* node)
{
if (node != nullptr) {
postorder(node->getLeft());
postorder(node->getRight());
std::cout << node->getData() << " ";
}
}
main.cpp
#include <iostream>
#include "BinaryTree.h"
using namespace std;
int main()
{
BinaryTree<char> tree;
BinaryNode<char>* d = new BinaryNode<char>('D', nullptr, nullptr);
BinaryNode<char>* e = new BinaryNode<char>('E', nullptr, nullptr);
BinaryNode<char>* b = new BinaryNode<char>('B', d, e);
BinaryNode<char>* f = new BinaryNode<char>('F', nullptr, nullptr);
BinaryNode<char>* c = new BinaryNode<char>('C', f, nullptr);
BinaryNode<char>* a = new BinaryNode<char>('A', b, c);
tree.setRoot(a);
tree.inorder();
tree.preorder();
tree.postorder();
cout << endl;
return 0;
}
결과는 다음과 같다.
노도 클래스에서 순회를 구현할수도 있다. 실제 중위 순회를 tree class가 아닌 node class 내부에 멤버함수로 구현할수 있다.
template <typename T>
void BinaryNode<T>::inorder() {
if (_left != nullptr) _left->inorder();
std::cout << _data << " ";
if (_right != nullptr) _right->inorder();
}
이를 사용하면 노드는 자신을 기준으로 순회연산을 처리할수 있다. 트리에서는 이와같이 노드 클래스에서 처리할 수 있는 연산이 많은데, 이는 모든 노드를 그 노드를 root로 하는 하나의 트리로 생각할 수 있기 때문이다.
따라서 더이상 tree class에서 inorder 함수는 필요가 없고, 인터페이스함수만 남기면 된다. 인터페이스 inorder함수는 수정되야 한다.
void inorder() {
std::cout << "\ninorder: ";
if (!isEmpty()) root->inorder();
}
레벨 순회
레벨 순회는 각 노드를 레벨 순으로 검사하는 방식이다. 우선 level1 의 노드를 검사후 level1 그다음 leve2, level3... 이런 방식으로 순회한다. 앞의 3가지 순회는 순환을 사용하니 stack이 상요되었지만, 레벨 순회의 경우 queue를 사용한다.
Level_order()
initialize queue;
queue.enqueue(root);
while queue.isEmpty() ≠ TRUE do
x <- queue.dequeue();
if(x ≠ NULL) then
print DATA(x);
queue.enqueue(LEFT(x));
queue.enqueue(RIGHT(x));
이진트리 노드 수 구하기
getCount(x)
if x = NULL
then return 0;
else return 1 + getCount(LEFT(x)) + getCount(RIGHT(x));
단말노드 수 구하기
getLeafCount(x)
if x = NULL then return 0;
if LEFT(x) = NULL and RIGHT(x) = NULL
then return 1;
else return getLeafCount(LEFT(x)) + getLeafCount(RIGHT(x));
높이 구하는 알고리즘
getHeight(x)
if x = NULL
then return 0;
else return 1 + max(getHeight(LEFT(x), getHeight(RIGHT(x));
수식 트리 계산 알고리즘
evaluate(exp)
if exp = NULL
then return 0;
else x<-evaluate(LEFT(x));
y<-evaluate(RIGHT(x));
op<-DATA(exp);
return (x op y);
2) 나의 현황
◆ Effective C++을 주문하였다. 이책도 하루에 2단원 정도씩 정리해서 올려볼까? 란 생각과 동시에, 너무 유명한 책이라 이미 정리된 블로그 글들이 많은데 그냥 내용공부에 치중해야겠다는 생각이 동시에 든다. 일단 빨리 읽고싶다!
'CS > Data Structure (2021-1)' 카테고리의 다른 글
C++로 쉽게 풀어쓴 자료구조 : 9장, 이진 탐색 트리 (0) | 2022.01.15 |
---|---|
C++로 쉽게 풀어쓴 자료구조 프로그래밍 프로젝트 8 (0) | 2022.01.14 |
[자료구조] C++로 쉽게 풀어쓴 자료구조 : 7장, 순환 (0) | 2022.01.14 |
[자료구조] C++로 쉽게 풀어쓴 자료구조 : 6장, List (0) | 2022.01.14 |
[자료구조] C++로 쉽게 풀어쓴 자료구조 프로그래밍 프로젝트 5 (0) | 2022.01.14 |
댓글