LongDz 的数字文墨提案

208. 实现 Trie (前缀树)

4 min

题目

实现 Trie (前缀树) 中等

解法:前缀树

思路

核心思想是前缀树,通过树形结构共享字符串前缀实现高效存储检索。每个节点代表一个字符,路径表示前缀,isEnd 标记单词结尾。

正确性基于路径唯一性:插入和查询遵循相同字符路径规则,确保数据一致性。插入时按需创建节点,查询时沿路径遍历,若路径存在且 isEnd 为真则命中,否则失败。

  • Step 1: 定义 TrieNode 结构,包含 26 个子节点指针和 isEnd 标志
  • Step 2: insert 遍历单词字符,创建缺失子节点,最后标记 isEnd=true
  • Step 3: search 遍历单词字符,路径中断返回 false,否则返回 isEnd
  • Step 4: startsWith 遍历前缀字符,路径中断返回 false,否则返回 true

复杂度

  • 时间复杂度: O(L)O(L),其中 L 为单词或前缀长度。每次操作需遍历字符串每个字符,每步操作耗时 O(1)O(1)
  • 空间复杂度: O(N×L)O(N \times L),N 为插入单词数,L 为平均长度。最坏情况下每个单词都无共享前缀,需 N×LN \times L 个节点。

代码

class Trie {
private:
    struct TrieNode {
        TrieNode* children[26];
        bool isEnd;
        TrieNode() : isEnd(false) {
            for (int i = 0; i < 26; i++) children[i] = nullptr;
        }
    };
    TrieNode* root;

public:
    Trie() {
        root = new TrieNode();
    }
    
    void insert(string word) {
        TrieNode* curr = root;
        for (char c : word) {
            int idx = c - 'a';
            if (!curr->children[idx]) {
                curr->children[idx] = new TrieNode();
            }
            curr = curr->children[idx];
        }
class Trie {
private:
    struct TrieNode {
        TrieNode* children[26];
        bool isEnd;
        TrieNode() : isEnd(false) {
            for (int i = 0; i < 26; i++) children[i] = nullptr;
        }
    };
    TrieNode* root;

public:
    Trie() {
        root = new TrieNode();
    }
    
    void insert(string word) {
        TrieNode* curr = root;
        for (char c : word) {
            int idx = c - 'a';
            if (!curr->children[idx]) {
                curr->children[idx] = new TrieNode();
            }
            curr = curr->children[idx];
        }
        curr->isEnd = true;
    }
    
    bool search(string word) {
        TrieNode* curr = root;
        for (char c : word) {
            int idx = c - 'a';
            if (!curr->children[idx]) {
                return false;
            }
            curr = curr->children[idx];
        }
        return curr->isEnd;
    }
    
    bool startsWith(string prefix) {
        TrieNode* curr = root;
        for (char c : prefix) {
            int idx = c - 'a';
            if (!curr->children[idx]) {
                return false;
            }
            curr = curr->children[idx];
        }
        return true;
    }
};

/**
 * Your Trie object will be instantiated and called as such:
 * Trie* obj = new Trie();
 * obj->insert(word);
 * bool param_2 = obj->search(word);
 * bool param_3 = obj->startsWith(prefix);
 */