티스토리 뷰

Algorithm

Trie (트라이)

snail voyager 2024. 8. 31. 00:14
728x90
반응형

Trie의 주요 특징

  • 문자 기반 트리: 각 노드는 문자를 저장하며, 루트 노드에서부터 각 문자 노드를 따라가면서 문자열을 구성합니다.
  • 공유된 경로: 문자열의 공통된 접두사를 공유하므로 메모리를 절약할 수 있습니다.
  • 효율적인 검색: 단어 검색, 삽입, 삭제가 매우 빠릅니다. 검색 시간은 문자열의 길이에 비례합니다.

Trie의 구조

  • 노드(Node): 각 노드는 하나의 문자 또는 값(보통은 문자)을 저장하며, 자식 노드들을 가리킵니다.
  • 루트 노드: 트리의 최상위 노드로, 빈 값(또는 특별한 값)을 가집니다.
  • 자식 노드: 각 노드는 여러 자식 노드를 가질 수 있으며, 자식 노드는 다른 문자로 이어지는 경로를 나타냅니다.
  • 종료 표시(End of Word): 노드에는 단어가 끝나는지 여부를 표시하는 플래그가 있을 수 있습니다. 이 플래그를 통해 트라이가 저장한 문자열이 해당 위치에서 끝나는지 확인할 수 있습니다.

삽입 및 검색 성능

  • 삽입 및 검색 시간 복잡도: 문자열의 길이를 L이라고 할 때, Trie에서 삽입 및 검색은 **O(L)**입니다. 즉, 문자열의 길이에 비례한 시간이 소요됩니다.
  • 접두사 검색: Trie는 접두사 검색에 최적화되어 있어, 특정 접두사로 시작하는 모든 단어를 빠르게 찾을 수 있습니다. 이는 해시 테이블로는 할 수 없는 작업입니다.

메모리 사용

  • 메모리 효율성: Trie는 메모리를 많이 사용합니다. 특히 저장하는 문자열이 많을수록 각 문자를 노드로 분리하여 저장하므로, 공간 복잡도가 커집니다. 그러나 문자열들 사이에 공통된 접두사가 많을 경우, 이 접두사를 공유하므로 일부 메모리를 절약할 수 있습니다.
  • 공통 접두사 공유: 공통 접두사들은 메모리를 공유하므로, 같은 접두사를 가진 단어들이 많을 때는 오히려 공간 효율이 좋아질 수 있습니다.

Trie 구현

class TrieNode {
    TrieNode[] children = new TrieNode[26]; // 알파벳 소문자 26개
    boolean isEndOfWord; // 해당 노드가 단어의 끝인지 여부

    TrieNode() {
        isEndOfWord = false;
        for (int i = 0; i < 26; i++) {
            children[i] = null;
        }
    }
}

class Trie {
    private TrieNode root;

    public Trie() {
        root = new TrieNode();
    }

    // 문자열 삽입
    public void insert(String word) {
        TrieNode node = root;
        for (int i = 0; i < word.length(); i++) {
            int index = word.charAt(i) - 'a'; // 'a'를 기준으로 인덱스 계산
            if (node.children[index] == null) {
                node.children[index] = new TrieNode();
            }
            node = node.children[index];
        }
        node.isEndOfWord = true;
    }

    // 문자열 검색
    public boolean search(String word) {
        TrieNode node = root;
        for (int i = 0; i < word.length(); i++) {
            int index = word.charAt(i) - 'a';
            if (node.children[index] == null) {
                return false;
            }
            node = node.children[index];
        }
        return node != null && node.isEndOfWord;
    }

    // 접두사 검색
    public boolean startsWith(String prefix) {
        TrieNode node = root;
        for (int i = 0; i < prefix.length(); i++) {
            int index = prefix.charAt(i) - 'a';
            if (node.children[index] == null) {
                return false;
            }
            node = node.children[index];
        }
        return true;
    }
}

Trie vs. Hash Table

Trie의 장점

  • 접두사 검색 최적화: Trie는 접두사 기반 검색에 매우 유리합니다. 예를 들어, 자동 완성 기능이나 사전에서 특정 접두사로 시작하는 단어를 찾는 데 적합합니다.
  • 순서 보존: 트라이 구조는 문자 순서를 보존하므로, 특정 순서에 따른 정렬된 데이터를 쉽게 관리할 수 있습니다.

Trie의 단점

  • 메모리 사용량: 문자열이 많아질수록 트라이의 노드 수가 증가하고, 메모리 사용이 커집니다. 특히, 저장할 단어들의 접두사가 공통된 부분이 없을 경우에는 비효율적일 수 있습니다.
  • 속도: 해시 테이블과 비교했을 때 문자열의 길이에 따라 검색 속도가 느릴 수 있습니다.

Hash Table의 장점

  • 빠른 검색과 삽입: 해시 테이블은 평균적으로 O(1)의 시간 복잡도로 매우 빠른 검색 및 삽입 성능을 제공합니다. 이로 인해 문자열 검색이 필요한 대부분의 일반적인 경우에 더 효율적일 수 있습니다.
  • 효율적인 메모리 사용: 해시 테이블은 문자열 전체를 저장하지만, 메모리 사용량은 보통 트라이보다 효율적입니다.

Hash Table의 단점

  • 접두사 검색 비효율: 해시 테이블은 접두사 기반 검색이 불가능하며, 모든 키를 검사해야 하므로 비효율적입니다.
  • 해시 충돌: 충돌이 발생하면 성능이 저하될 수 있으며, 해시 함수가 적절하지 않으면 최악의 경우 O(n)까지 성능이 떨어질 수 있습니다.

언제 Trie가 Hash Table보다 더 좋은가?

  • 접두사 검색이 빈번한 경우: 예를 들어, 자동 완성 기능을 구현하거나 사전에서 특정 접두사로 시작하는 단어를 찾을 때 Trie가 유리합니다.
  • 정렬된 데이터를 다룰 때: 트라이는 알파벳 순서대로 데이터를 저장할 수 있어, 자동으로 정렬된 결과를 얻는 것이 가능합니다.

언제 Hash Table이 더 좋은가?

  • 단순한 검색과 삽입: 검색 및 삽입만 필요하고 접두사 검색이 필요하지 않은 경우 해시 테이블이 더 빠르고 메모리 효율적일 수 있습니다.
  • 데이터 크기가 크고, 검색이 빈번한 경우: 큰 데이터를 다루면서 접두사 검색이 필요하지 않다면 해시 테이블이 성능 면에서 더 나을 수 있습니다.
728x90
반응형

'Algorithm' 카테고리의 다른 글

Kadane's Algorithm  (0) 2024.09.22
Divide and Conquer vs. Dynamic Programming  (0) 2024.09.22
위상 정렬 (Topological Sort)  (0) 2022.10.01
멱집합 Power Set  (0) 2020.04.20
순열, 조합, 중복순열, 중복조합  (0) 2020.04.16
반응형
300x250