Make Unreal REAL.
article thumbnail
코딩 테스트를 위한 자료 구조와 알고리즘 with C++

 

 

트리의 주요 용어

  • 루트(Root)가 가장 위에 있는 상하 반전 형태이다.
  • 노드(Node)와 간선(Edge)을 활용해 계층을 구성한다.
  • 부모(Parent) 노드와 자식(Child) 노드를 가질 수 있다.
  • 부모의 다른 자식 노드를 형제(Sibling) 노드라고 한다.
  • 특정 노드를 루트로 하는 부트리(Subtree)가 존재한다.
  • 자식이 없는 가장 끝의 노드들을 단말(Leaf) 노드라고 한다.
  • 단말 노드가 아닌 노드를 내부(Internal) 노드라고 한다.
  • 부모 노드와 그 부모들을 선조(Ancestor)라고 한다.
  • 자식 노드와 그 자식들을 자손(Descendant)라고 한다.
  • 자신을 포함한 모든 자손 노드의 개수를 노드의 크기(Size)라고 한다.
  • 루트에서 해당 특정 노드까지의 간선 개수를 노드의 깊이(Depth)라고 한다.
  • 특정 깊이를 갖는 노드의 집합을 레벨(Level)이라고 한다.
  • 특정 노드의 부트리 혹은 자식 노드의 개수를 노드의 차수(Degree)라고 한다.
  • 트리의 최대 차수를 트리의 차수(Degree of tree)라고 한다.
  • 트리의 최대 깊이를 트리의 높이(Height)라고 한다.
  • 서로 독립인 트리의 집합을 숲(Forest)이라고 한다.

 

트리의 특징

  • 계층 구조이다.
  • 연결된 비순환 그래프(Connected Acyclic Graph)의 한 종류이다.
    (모든 노드가 연결되어 있으며 루프가 존재하지 않는다.)
  • N개의 노드로 구성된 트리는 N - 1개의 간선을 갖는다.
  • 루트에서 특정 노드로 가는 경로(Path)는 유일하다.
  • 전위순회, 중위순회, 후위순회를 통해 순회할 수 있다.

 

 

트리의 순회

  • 전위 순회 (Pre-order)
    부모 노드 > 왼쪽 자식 > 오른쪽 자식의 우선순위를 가지며 부모 노드를 먼저 방문
  • 중위 순회 (In-order)
    왼쪽 자식 > 부모 노드 > 오른쪽 자식의 우선순위를 가지며 부모 노드를 중간에 방문
  • 후위 순회 (Post-order)
    왼쪽 자식 > 오른쪽 자식 > 부모 노드의 우선순위를 가지며 부모 노드를 마지막에 방문
  • 레벨 순회 (Level-order)
    부모 노드 -> 왼쪽 자식 -> 오른쪽 자식 순으로 낮은 레벨의 왼쪽부터 차례로 방문한다.

 

 

깊이 우선 탐색(Depth-First Search)과 너비 우선 탐색(Breadth-First Search)

  • 깊이 우선 탐색(DFS)
    순회 우선순위에 따라 가장 깊이가 깊은 노드부터 재귀적으로 방문한다.
  • 너비 우선 탐색(BFS)
    자식을 먼저 확인하여 같은 레벨을 우선으로 방문한다.

 

#include <iostream>
#include <queue>

using namespace std;

struct node
{
    string position;    // 직책
    node* first;        // 첫 번째 부하 직원
    node* second;       // 두 번째 부하 직원
};

struct org_tree
{
    node* root;

    // 조직 생성 함수
    static org_tree create_org_structure(const string& pos)
    {
        org_tree tree;
        tree.root = new node{pos, nullptr, nullptr};

        return tree;
    }

    // 직책으로 검색 (전위순회)
    static node* find(node* root, const string& pos)
    {
        node* first;

        if (root == nullptr)
            return nullptr;

        if (root->position == pos)
            return root;

        if ((first = find(root->first, pos)) != nullptr)
            return first;
        else
            return find(root->second, pos);
    }

    // manager 직책을 갖는 상사의 부하 직원으로 subordinate 추가
    bool addSubordinate(const string& manager, const string& subordinate)
    {
        node* managerNode = find(root, manager);
        node* subordinateNode;

        // 해당 직책의 상사가 존재하지 않는다.
        if (managerNode == nullptr)
            return false;

        // 이미 2명의 부하 직원을 갖고 있다.
        if (managerNode->first && managerNode->second)
            return false;

        subordinateNode = new node{subordinate, nullptr, nullptr};

        if (managerNode->first == nullptr)  // 첫 번째 부하 직원으로 삼을 수 있다.
            managerNode->first = subordinateNode;
        else    // 첫 번째 부하 직원은 있으므로 두 번째로 삼을 수 있다.
            managerNode->second = subordinateNode;

        return true;
    }

    // 전위 순회
    static void preOrder(node* start)
    {
        if (start == nullptr)
            return;

        cout << start->position << ", ";
        preOrder(start->first);
        preOrder(start->second);
    }

    // 중위 순회
    static void inOrder(node* start)
    {
        if (start == nullptr)
            return;

        preOrder(start->first);
        cout << start->position << ", ";
        preOrder(start->second);
    }

    // 후위 순회
    static void postOrder(node* start)
    {
        if (start == nullptr)
            return;

        preOrder(start->first);
        preOrder(start->second);
        cout << start->position << ", ";
    }

    // 레벨 순회
    static void levelOrder(node* start)
    {
        if (start == nullptr)
            return;

        queue<node*> que;
        que.push(start);

        while (!que.empty())
        {
            size_t size = que.size();
            
            for (int i = 0; i < size; ++i)
            {
                node* current = que.front();
                que.pop();

                // 부모 노드를 방문하고
                cout << current->position << ", ";

                // 자식 노드를 큐에 삽입한다.
                if (current->first)
                    que.push(current->first);

                if (current->second)
                    que.push(current->second);

                // front에 가까울 수록 레벨이 낮은 노드가 위치한다.
            }

            cout << endl;
        }
    }
};

void main()
{
    org_tree tree = org_tree::create_org_structure("CEO");

    tree.addSubordinate("CEO", "부사장");
    tree.addSubordinate("부사장", "IT부장");
    tree.addSubordinate("부사장", "마케팅부장");
    tree.addSubordinate("IT부장", "보안팀장");
    tree.addSubordinate("IT부장", "앱개발팀장");
    tree.addSubordinate("마케팅부장", "물류팀장");
    tree.addSubordinate("마케팅부장", "홍보팀장");
    tree.addSubordinate("부사장", "재무부장");

    cout << "[전위 순회]" << endl;
    org_tree::preOrder(tree.root); cout << endl << endl;

    cout << "[중위 순회]" << endl;
    org_tree::inOrder(tree.root); cout << endl << endl;

    cout << "[후위 순회]" << endl;
    org_tree::postOrder(tree.root); cout << endl << endl;

    cout << "[레벨 순회]" << endl;
    org_tree::levelOrder(tree.root);
}

 

출력

[전위 순회]
CEO, 부사장, IT부장, 보안팀장, 앱개발팀장, 마케팅부장, 물류팀장, 홍보팀장,

[중위 순회]
부사장, IT부장, 보안팀장, 앱개발팀장, 마케팅부장, 물류팀장, 홍보팀장, CEO,

[후위 순회]
부사장, IT부장, 보안팀장, 앱개발팀장, 마케팅부장, 물류팀장, 홍보팀장, CEO,

[레벨 순회]
CEO,
부사장,
IT부장, 마케팅부장,
보안팀장, 앱개발팀장, 물류팀장, 홍보팀장,
profile

Make Unreal REAL.

@diesuki4

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그