트라이는 "문자열을 빠르게 탐색하게 해주는 자료구조" 이다. 즉, '문자열'을 관리하는 방법 중의 하나다
작동 원리
트라이는 주어진 문자열을 이루고 있는 문자를 앞에서 부터 하나씩 노드를 생성해가면서 만들어 진다.
이 과정에서 반복된 재귀 호출이 이루어지는데, 말로는 무슨 말인지 모르겠으니, 이 과정을 구체적으로 알아보자.
먼저, 문자열을 트라이로 생성하는 과정부터 알아보자. 간단하게 순서를 붙여보자면 다음과 같이 진행된다.
( 지금부터 '루트(Root)' 라는 말이 나올 수 있는데, 이는 가장 초기 노드라 생각하면 된다.
즉, 연결되어 있는 어떠한 노드도 없는 초기 노드이다. )
1. 주어진 문자열에서 현재 문자를 가져온다.
2. 현재 문자로 이루어진 노드가 존재한다면, 그 노드로 그 다음 문자열을 탐색하고,
노드가 없다면 그 노드를 새로 할당 받은 후, 해당 노드를 통해 다음 문자열을 탐색한다.
3. 문자열의 마지막이 될 때까지 위의 과정을 반복한다.
위의 과정을 그림으로 한번 알아보자.
문자열, [ ABC , ABCD , BCG , ZYX ,BDE ] 를 순차적으로 삽입하는 과정을 알아보자.
가장 초기, 루트노드에는 아무런 값도, 노드도 존재하지 않는다. 즉, 다음과 같은 상태일 것이다.
그럼, "ABC" 부터 트라이에 삽입해보자.
1. 주어진 문자열에서 현재 문자를 가져온다.
지금 주어진 문자열 'ABC'에서 아직 아무런 문자도 삽입하지 않았으므로, 현재 문자는 가장 앞에 있는 문자인 'A'를 가르키고 있을 것이다.
2. 현재 문자로 이루어진 노드가 존재한다면, 그 노드로 그 다음 문자열을 탐색하고, 노드가 없다면 그 노드를 새로 할당 받은 후, 해당 노드를 통해 다음 문자열을 탐색한다.
구현
struct Trie {
bool finish; // 끝나는 지점을 표시해줌
Trie* next[26]; // 26가지 알파벳에 대한 트라이
// 생성자
Trie() : finish(false) {
memset(next, 0, sizeof(next));
}
// 소멸자
~Trie() {
for (int i = 0; i < 26; i++)
if (next[i])
delete next[i];
}
// 트라이에 문자열 삽입
void insert(const char* key) {
if (*key == '\0')
finish = true; // 문자열이 끝나는 지점일 경우 표시
else {
int curr = *key - 'A';
if (next[curr] == NULL)
next[curr] = new Trie(); // 탐색이 처음되는 지점일 경우 동적할당
next[curr]->insert(key + 1); // 다음 문자 삽입
}
}
// 트라이에서 문자열 찾기
Trie* find(const char* key) {
if (*key == '\0') return this; // 문자열이 끝나는 위치를 반환
int curr = *key - 'A';
if (next[curr] == NULL) return NULL; // 찾는 값이 존재하지 않음
return next[curr]->find(key + 1); // 다음 문자를 탐색
}
};
시간 및 공간 복잡도
문자열의 최대 길이가 n일 때, Find와 Insert 모두 문자열의 길이(트리의 최대 높이) 내로 끝나기 때문에 O(n)의 시간 복잡도를 가지는 것을 알 수 있다.
위의 시간 복잡도 O(n)을 가지기 위해서는 다음 문자를 가리키는 노드가 필요하다. 예를 들어, 숫자에 대해 트라이를 만들어야 한다면 0~9, 총 10개의 포인터 배열이 있어야하며, 알파벳 소문자에 대해 트라이를 만든다면 a~z인 총 26개의 포인터 배열을 가지고 있어야 한다.
즉, 최종 메모리는 O(포인터의 크기 * 포인터 배열의 개수 * 트라이에 존재하는 총 노드의 개수)가 된다.
Radix tree (Compact prefix tree)
트라이의 치명적인 단점인 공간에 대한 효율성을 최적화하기 위한 방법 중 하나이다. 아래 그림처럼 자식 노드가 하나밖에 없는 경우, 부모 노드에 모두 합치는 방식으로 필요한 메모리를 줄일 수 있다. 이 글에서는 따로 구현하는 방식은 설명하지 않는다.
추천 문제
[Gold 2] 개미굴
[Platinum 5] Prefix와 Suffix
[Platinum 3] 별다줄 / 휴대폰 자판 / 용량 부족
[Platinum 2] 아름다운 이름 / 문자열 집합 판별
출처 : [ 자료구조 트라이(TRIE) ] 개념과 구현방법 (C++) :: 얍문's Coding World.. (tistory.com)
출처: https://eun-jeong.tistory.com/29 [흔들리며 피는 꽃:티스토리]
'CS > 자료구조 & 알고리즘' 카테고리의 다른 글
[C++] Radix Sort (기수 정렬) (0) | 2023.12.04 |
---|---|
[C++] Trie (트라이) 소스코드 (0) | 2023.12.01 |
[C++] const map 객체에 [key] 접근시 에러 (0) | 2023.11.22 |
[C++] vector resize 시 주의할 점 (0) | 2023.11.22 |
Brute Force (브루트 포스) 알고리즘 (0) | 2023.11.22 |