Trie란 Prefix Tree나 Radix Tree라고도 불리는 트리 형태의 자료구조로, reTRIEval 에서 유래한 이름을 가지고 있다.

Trie는 각 노드가 string으로 된 key를 나타내고, 각 엣지 (간선)가 하나의 알파벳을 나타내는 트리 형태의 자료구조로, key-value 쌍을 저장하도록 하면 map처럼 associative array로 이용할 수 있고, 그냥 key만 저장하면 어떤 string이 집합에 속하는지 확인하기 위한 set처럼 삽입/삭제가 가능한 집합 자료구조로 이용할 수 있다.

흔히 사용되는 std::map/std::set의 내부 구현인 Red-black tree (balanced binary search tree/self-balancing binary search tree)와 비교해보면 각 연산 (검색, 삽입)이 다음과 같은 시간 복잡도를 갖는다 ($L$: key로 이용되는 문자열의 최대 길이, $N$: key의 개수, 즉 자료구조의 원소 개수):

연산 BBST Trie
삽입 $O(L\mathrm{log}N)$ $O(L)$
검색 $O(L\mathrm{log}N)$ $O(L)$

{"AA", "AC", "BC", "BCG", "C", "CC"} 의 문자열을 원소로 갖는 Trie를 그림으로 나타내면 다음과 같다:

$\otimes$로 표현되는 노드는 해당 노드로 끝나는 문자열이 존재한다는 의미인데, 예를 들어 이 집합에 "A"는 존재하지 않지만 "AA"는 존재하므로 "AA"를 나타내는 노드에서는 $\otimes$로 표현되는 노드로의 간선이 존재하지만 "A"를 나타내는 노드에서는 간선이 존재하지 않는 것을 확인할 수 있다. (이후 $\otimes$ 로 나타나는 노드로 연결되는 간선을 "$"로 표현)

연산의 경우 BST처럼 삽입 과정이 검색과 굉장히 닮아있기 때문에, 검색부터 살펴보도록 하자.

  1. "BCG"를 검색하는 경우
  2. "A"를 검색하는 경우
  3. 삽입
  4. 삭제

"BCG"를 검색하는 경우

루트에서 시작해서, 해당 알파벳을 나타내는 간선을 따라 쭉쭉 따라가면 끝. 단, "BGC""BCG$"로 고려해야 한다. 만약 중간에 NULL을 만나게 된다면 (존재하지 않는 간선을 선택하게 된다면) 해당 문자열은 집합에 존재하지 않는다는 결론이 나오고, 그렇지 않을 경우 해당 문자열이 집합에 존재한다는 것을 확인할 수 있다.

"A"를 검색하는 경우

마찬가지로 "A$"를 생각하면, 중간에 $을 나타내는 간선이 존재하지 않아 실패하는 것을 볼 수 있다.

삽입

검색을 보면서 깨달았겠지만, 삽입은 검색을 하는 도중 실패한 부분부터 간선을 추가해나가는 식으로 반복하면 간단!

삭제

삭제 역시도 각 노드에 rereference count 를 관리하면서 0이 되는 순간 노드와 간선을 지워주는 형태로 구현하면 간단하다.