1. 개념

계층적 구조를 가지는 데이터를 표현하기 위한 자료 구조

(1) 노드(Node) : 각 데이터를 표현하는 정점

(2) 간선(Edge) : 노드의 계층 구조를 표현하기 위해 사용하는 화살표

 

2. 관련 용어

- 부모 노드, 자식 노드, 형제 노드

- 선조, 자손

- 루트 ; 최상위 노드, 잎 ; 최하위 노드

- 깊이 ; 루트에서 어떤 노드에 도달하기 위해 거쳐야 하는 간선의 수

- 높이 ; 가장 깊숙히 있는 노드의 깊이

- 트리의 재귀적 속성 ; 트리의 일부분만 잘라도 트리의 속성을 띄고 있음

- 서브트리

 

3. 코드로 작성해보기

(1) 노드(Node)

#include <iostream>
#include <vector>
#include <memory> // shared_ptr 사용
#include <list>
#include <queue>
#include <stack>

using namespace std;
using NodeRef = shared_ptr<struct Node>;

struct Node
{
	Node() { }
	Node(const string& data) : data(data) { }


	string data;
	vector<NodeRef> children;
};

 

(2) NodeRef ; shared_ptr을 사용한 Node의 참조형

NodeRef를 사용하여 각 노드가 자기 자신을 가리키는 스마트 포인터를 가지게 한다.

이를 통해 노드끼리 안전하게 참조하고 소멸 시에 적절히 메모리를 해제하게 된다.

 

(3) CreateTree ; 본격적인 Tree 만들기

NodeRef CreateTree()
{
	NodeRef root = make_shared<Node>("R1 개발실");
	{
		NodeRef node = make_shared<Node>("디자인팀");
		root->children.push_back(node);
		{
			NodeRef leaf = make_shared<Node>("전투");
			node->children.push_back(leaf);
		}
		{
			NodeRef leaf = make_shared<Node>("경제");
			node->children.push_back(leaf);
		}
		{
			NodeRef leaf = make_shared<Node>("스토리");
			node->children.push_back(leaf);
		}
	}

	{
		NodeRef node = make_shared<Node>("프로그래밍팀");
		root->children.push_back(node);
		{
			NodeRef leaf = make_shared<Node>("서버");
			node->children.push_back(leaf);
		}
		{
			NodeRef leaf = make_shared<Node>("클라");
			node->children.push_back(leaf);
		}
		{
			NodeRef leaf = make_shared<Node>("엔진");
			node->children.push_back(leaf);
		}
	}

	{
		NodeRef node = make_shared<Node>("아트팀");
		root->children.push_back(node);
		{
			NodeRef leaf = make_shared<Node>("배경");
			node->children.push_back(leaf);
		}
		{
			NodeRef leaf = make_shared<Node>("캐릭터");
			node->children.push_back(leaf);
		}
	}

	return root; // 최상위 노드를 이용하여 순회할 수 있음
}

각 노드들은 children이라는 멤버 변수(동적 배열)를 가지고 있다.

root - node - leaf의 종속 관계에서 root의 children은 node, node의 children은 leaf로 지정한다.

그리고 Node의 오버로딩된 생성자를 이용하여 값을 data 멤버변수에 넣어준다.

 

(4) PrintTree ; 트리 출력하기

void PrintTree(NodeRef root, int depth)
{
	for (int i = 0; i < depth; i++)
		cout << "-";
	cout << root->data << endl;

	for (const NodeRef& child : root->children)
		PrintTree(child, depth + 1);
}

범위 기반(range-based) for 루프를 사용하여 root 노드의 children 벡터에 있는 각 자식 노드를 순회한다.

root -> children에서 가져온 각 자식 노드를 child라는 변수에 할당하고, 루프 내에서 PrintTree 함수를 재귀적으로 호출한다.

그리고 각 자식 노드에 대해 '-'를 하나씩 증가시킨 depth + 1 값을 전달하여 하위 트리를 출력한다.

 

(5) GetHeight ; 높이 구하기

int GetHeight(NodeRef root)
{
	int height = 1;

	for (NodeRef& child : root->children)
	{ // 각 자식마다 높이가 다를 수 있음에 유의
		height = max(height, GetHeight(child) + 1); // 자식 트리의 높이에 1을 더하면 전체 트리의 높이
	}

	return height;
}

자식 트리의 높이를 구해서 1을 더해주면 전체 트리의 높이가 된다.

max를 이용하는 이유는 자식 노드마다 높이가 다를 수 있기 때문에, 최하위에서부터 트리의 높이를 측정하기 위함이다.

 

(6) main

int main()
{
	NodeRef root = CreateTree();

	PrintTree(root, 0);

	return 0;
}

 

 

'자료구조와 알고리즘 > 힙과 우선순위 큐' 카테고리의 다른 글

3-2. 힙 트리(Heap Tree)  (0) 2023.07.10