※ 선형 자료구조

앞서 우리가 배운 것들은 모두 선형 자료구조에 속하는데 대표적으로 다음과 같다.

- stack, queue, deque, list . . . 

이와 달리 선형이 아닌 "계층적 구조 (hierarchical)"를 표현하기 위해 사용되는 자료구조인 트리(tree)에 대해 알아보고자 한다.

 

컴퓨터 저장장치의 디렉토리 구조도 계층적인 자료구조로 많은 상황에서 사용되기에 자료구조를 배우는 이유중 하나라 할 수 있다.

자료구조의 양대산맥이라 생각될 정도로 그래프만큼 많이 사용되며 자주 사용되는 자료구조이기에 잘 익혀두자!

 

 

※ 트리 용어 

트리의 구성요소로는 루트(root), 서브트리(subtree), 간선(edge), 노드(node) 등이 있다.

- 차수(degree): 어떤 node가 갖는 자식노드의 개수

- 레벨(level): 트리의 층 수로 root를 level 0이나 1로 매긴다.(책마다 조금씩 다름)

- 높이(height): 트리의 각 층에 번호를 매기는 것으로 트리의 높이는 최대 레벨을 통해 결정할 수 있다.

 

 

※ 이진트리 (Binary Tree)

모든 노드의 차수가 2 이하이며 왼쪽 서브트리와 오른쪽 서브트리가 반드시 구별되어야 한다.

n개의 노드는 n-1개의 간선(edge)를 가지며 높이가 h인 이진트리의 최대 노드 개수는 2^h - 1이다.
n개의 노드를 갖는 이진트리의 높이는 다음과 같다. ⎡log(n+1)⎤ ≤ h ≤ n
포화이진트리(full binary tree): 모든 레벨의 노드가 꽉 차있는 트리, 높이가 h라면 총 2^h-1개의 노드를 갖는다.
완전이진트리(complete binary tree): 레벨 1~h-1까지는 모두 노드가 채워져 있고 마지막 레벨 h는 왼쪽~>오른쪽으로 순서대로 채워진 트리이다. (즉, 마지막 레벨의 노드가 꽉 차있지 않아도 됨)

대표적인 완전이진트리 예로는 힙(heap)이 있다.

 

 

 

※ 이진트리 순회 알고리즘

✯ 전위 순회 (preorder)

preorder(x)

if x != null
	then print data(x);	// 루트(x)노드 처리 
    	 preorder(LEFT(x));  // 왼쪽 subtree 방문
         preorder(RIGHT(x)); // 오른쪽 subtree 방문

 

✯ 중위 순회 (inorder)

inorder(x)

if x != null
	then inorder(LEFT(x));	// 왼쪽 subtree 방문
         print data(x);     // 루트(x)노드 처리
    	 inorder(RIGHT(x)); // 오른쪽 subtree 방문

 

✯ 후위 순회 (postorder)

postorder(x)

if x != null
	then postorder(LEFT(x)); // 왼쪽 subtree 방문
         postorder(RIGHT(x)); // 오른쪽 subtree 방문
         print data(x);     // 루트(x)노드 처리

 

 

 

 

 

※ 이진트리 노드 개수 및 높이 구하는 알고리즘

✯ node 개수 구하기

getCount(x)

if x == null
	then return 0;
    else return 1 + getCount(LEFT(x)) + getCount(RIGHT(x));

 

✯ leaf node 개수 구하기

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));

 

✯ height 구하기

getHeight(x)

if x == null
	then return 0;
    else return 1 + max(getHeight(LEFT(x)), getHeight(RIGHT(x)));

 

 

 

 

 

 

※ 이진트리 구현

✯ BinaryNode.h

#include <iostream>

class BinaryNode{
protected:
    int data;
    BinaryNode* left;
    BinaryNode* right;
public:
    BinaryNode(int val = 0, BinaryNode *l = nullptr, BinaryNode *r = nullptr) : data(val), left(l), right(r) { }
    void setData(int val) { data = val; }
    void setLeft(BinaryNode* l) { left = l; }
    void setRight(BinaryNode* r) { right = r; }
    int getData() const { return data; }
    BinaryNode* getLeft() const { return left; }
    BinaryNode* getRight() const { return right; }
    bool isLeaf() const { return left == nullptr && right == nullptr; }
};

 

✯ BinaryTree.h

#include "BinaryNode.h"
using namespace std;

class BinaryTree {
private:
    BinaryNode *root;
public:
    BinaryTree() : root(nullptr) { };
    void setRoot(BinaryNode * node) { root = node; }
    BinaryNode * getRoot() { return root; }
    bool isEmpty() { return root == nullptr; }

    // 이진트리의 순회연산 (전위, 중위, 후위, 레벨 순회)
    void inorder() { cout << endl << "inorder: "; inorder(root); }
    void inorder(BinaryNode *node) {
        if (node != nullptr) {
            inorder(node->getLeft());
            cout << node->getData() << " ";
            inorder(node->getRight());
        }
    }
    void preorder() { cout << endl << "preorder: "; preorder(root); }
    void preorder(BinaryNode *node) {
        if (node!= nullptr) {
            cout << node->getData() << " ";
            preorder(node->getLeft());
            preorder(node->getRight());
        }
    }
    void postorder() { cout << endl << "postorder: "; postorder(root); }
    void postorder(BinaryNode *node) {
        if (node!= nullptr) {
            postorder(node->getLeft());
            postorder(node->getRight());
            cout << node->getData() << " ";
        }
    }

    // 이진트리의 추가 연산 (이진트리 노드의 개수 구하기 ,단말노드개수 구하기, 높이 구하기)
    int getCount() { return isEmpty() ? 0 : getCount(root); }
    int getCount(BinaryNode *node) {
        if (node == nullptr) return 0;
        return 1 + getCount(node->getLeft()) + getCount(node->getRight());
    }
    int getLeafCount() { return isEmpty() ? 0 : getLeafCount(root); }
    int getLeafCount(BinaryNode *node) {
        if (node == nullptr) return 0;
        if (node->getLeft()) return 1;
        else
            return getLeafCount(node->getLeft()) + getLeafCount(node->getRight());
    }
    int getHeight() { return isEmpty() ? 0 : getHeight(root); }
    int getHeight(BinaryNode *node) {
        if (node == nullptr) return 0;
        int hLeft = getHeight(node->getLeft());
        int hRight = getHeight(node->getRight());
        return (hLeft > hRight) ? hLeft + 1 : hRight + 1;
    }
};

 

✯ main.cpp

#include "BinaryTree.h"
#include <iostream>
using namespace std;

int main() {
    BinaryTree tree;
    BinaryNode *d = new BinaryNode('D', nullptr, nullptr);
    BinaryNode *e = new BinaryNode('E', nullptr, nullptr);
    BinaryNode *b = new BinaryNode('B', d, e);
    BinaryNode *f = new BinaryNode('F', nullptr, nullptr);
    BinaryNode *c = new BinaryNode('C', f, nullptr);
    BinaryNode *a = new BinaryNode('A', b, c);
    tree.setRoot(a);

    tree.inorder();
    tree.preorder();
    tree.postorder();
    cout << endl;

    cout << "노드의 개수 = " << tree.getCount() << endl;
    cout << "leaf의 개수 = " << tree.getLeafCount() << endl;
    cout << "tree의 높이 =  " << tree.getHeight() << endl;


    /********************************************************************/
    //inorder: 68 66 69 65 70 67
    //preorder: 65 66 68 69 67 70
    //postorder: 68 69 66 70 67 65
    //노드의 개수 = 6
    //leaf의 개수 = 1
    //tree의 높이 =  3
}

 

 

+ Recent posts