【字符串】字典树 (Trie)

1. 深入理解字典树

1.1 核心思想与数据结构本质

字典树(Trie)是一种空间换时间的典型数据结构,其核心思想是利用字符串的公共前缀来优化存储和查询

数据结构本质

  • 字典树是一棵多叉树,每个节点的子节点数量取决于字符集大小
  • 它将字符串的路径信息编码到树的结构中
  • 通过路径压缩技术减少冗余存储

与普通树的区别

  • 普通树:节点存储数据
  • 字典树:边存储字符,路径表示字符串

1.2 字典树的数学模型

从数学角度看,字典树可以看作是一个有限状态自动机

  • 状态:树中的节点
  • 转移函数:字符到子节点的映射
  • 接受状态:标记为单词结尾的节点

对于一个包含 n 个字符串的集合,设平均字符串长度为 m,则:

  • 时间复杂度:O(n×m) 用于构建,O(m) 用于查询
  • 空间复杂度:最坏情况 O(σ×n×m),其中 σ 是字符集大小

2. 字典树的详细结构设计

2.1 节点结构的多种实现方式

#include <iostream>
#include <vector>
#include <unordered_map>
#include <memory>
#include <string>
using namespace std;

// 方案1:固定数组(小写字母a-z)
class TrieNodeArray {
public:
    static const int ALPHABET_SIZE = 26;
    bool isEnd;
    TrieNodeArray* children[ALPHABET_SIZE];
    
    TrieNodeArray() : isEnd(false) {
        // 初始化所有子节点为空
        for (int i = 0; i < ALPHABET_SIZE; i++) {
            children[i] = nullptr;
        }
    }
    
    // 字符转索引
    int charToIndex(char c) {
        return c - 'a';
    }
    
    // 索引转字符
    char indexToChar(int idx) {
        return 'a' + idx;
    }
};

// 方案2:哈希表(通用,支持任意字符)
class TrieNodeMap {
public:
    bool isEnd;
    unordered_map<char, TrieNodeMap*> children;
    
    TrieNodeMap() : isEnd(false) {}
};

// 方案3:向量(平衡空间和时间)
class TrieNodeVector {
public:
    bool isEnd;
    vector<pair<char, TrieNodeVector*>> children;
    
    TrieNodeVector() : isEnd(false) {}
    
    // 查找子节点
    TrieNodeVector* findChild(char c) {
        for (auto& pair : children) {
            if (pair.first == c) {
                return pair.second;
            }
        }
        return nullptr;
    }
    
    // 添加子节点
    void addChild(char c, TrieNodeVector* node) {
        children.push_back({c, node});
    }
};

// 方案4:智能指针版本(现代C++风格)
class TrieNodeSmart {
public:
    bool isEnd;
    unordered_map<char, unique_ptr<TrieNodeSmart>> children;
    
    TrieNodeSmart() : isEnd(false) {}
};

2.2 完整的Trie类设计

template<typename NodeType>
class Trie {
private:
    unique_ptr<NodeType> root;
    
    // 递归销毁
    void destroyTrie(NodeType* node) {
        if (!node) return;
        
        // 对于哈希表和向量版本需要特殊处理
        if constexpr (is_same_v<NodeType, TrieNodeMap> || 
                     is_same_v<NodeType, TrieNodeVector>) {
            for (auto& pair : node->children) {
                destroyTrie(pair.second);
            }
        } 
        else if constexpr (is_same_v<NodeType, TrieNodeArray>) {
            for (int i = 0; i < NodeType::ALPHABET_SIZE; i++) {
                if (node->children[i]) {
                    destroyTrie(node->children[i]);
                }
            }
        }
        
        delete node;
    }
    
    // 查找节点的模板实现
    template<typename T = NodeType>
    T* findNodeImpl(const string& str, T* current) {
        for (char c : str) {
            if constexpr (is_same_v<T, TrieNodeArray>) {
                int idx = current->charToIndex(c);
                if (idx < 0 || idx >= T::ALPHABET_SIZE || !current->children[idx]) {
                    return nullptr;
                }
                current = current->children[idx];
            }
            else if constexpr (is_same_v<T, TrieNodeMap>) {
                auto it = current->children.find(c);
                if (it == current->children.end()) {
                    return nullptr;
                }
                current = it->second;
            }
            else if constexpr (is_same_v<T, TrieNodeVector>) {
                current = current->findChild(c);
                if (!current) return nullptr;
            }
        }
        return current;
    }
    
public:
    Trie() {
        root = make_unique<NodeType>();
    }
    
    ~Trie() {
        if (root) {
            destroyTrie(root.get());
        }
    }
    
    // 插入操作
    void insert(const string& word) {
        NodeType* current = root.get();
        
        for (char c : word) {
            if constexpr (is_same_v<NodeType, TrieNodeArray>) {
                int idx = current->charToIndex(c);
                if (!current->children[idx]) {
                    current->children[idx] = new TrieNodeArray();
                }
                current = current->children[idx];
            }
            else if constexpr (is_same_v<NodeType, TrieNodeMap>) {
                if (current->children.find(c) == current->children.end()) {
                    current->children[c] = new TrieNodeMap();
                }
                current = current->children[c];
            }
            else if constexpr (is_same_v<NodeType, TrieNodeVector>) {
                TrieNodeVector* child = current->findChild(c);
                if (!child) {
                    child = new TrieNodeVector();
                    current->addChild(c, child);
                }
                current = child;
            }
        }
        
        current->isEnd = true;
    }
    
    // 搜索操作
    bool search(const string& word) {
        NodeType* node = findNodeImpl(word, root.get());
        return node && node->isEnd;
    }
    
    // 前缀搜索
    bool startsWith(const string& prefix) {
        return findNodeImpl(prefix, root.get()) != nullptr;
    }
    
    // 获取所有以prefix开头的单词
    vector<string> getWordsWithPrefix(const string& prefix) {
        vector<string> result;
        NodeType* node = findNodeImpl(prefix, root.get());
        
        if (node) {
            collectWords(node, prefix, result);
        }
        
        return result;
    }
    
private:
    void collectWords(NodeType* node, const string& currentWord, vector<string>& result) {
        if (node->isEnd) {
            result.push_back(currentWord);
        }
        
        if constexpr (is_same_v<NodeType, TrieNodeArray>) {
            for (int i = 0; i < NodeType::ALPHABET_SIZE; i++) {
                if (node->children[i]) {
                    char c = node->indexToChar(i);
                    collectWords(node->children[i], currentWord + c, result);
                }
            }
        }
        else if constexpr (is_same_v<NodeType, TrieNodeMap>) {
            for (auto& pair : node->children) {
                collectWords(pair.second, currentWord + pair.first, result);
            }
        }
        else if constexpr (is_same_v<NodeType, TrieNodeVector>) {
            for (auto& pair : node->children) {
                collectWords(pair.second, currentWord + pair.first, result);
            }
        }
    }
};

3. 高级功能实现

3.1 带权重的字典树

class WeightedTrieNode {
public:
    int weight;  // 权重(如词频)
    bool isEnd;
    unordered_map<char, unique_ptr<WeightedTrieNode>> children;
    
    WeightedTrieNode() : weight(0), isEnd(false) {}
};

class WeightedTrie {
private:
    unique_ptr<WeightedTrieNode> root;
    
public:
    WeightedTrie() : root(make_unique<WeightedTrieNode>()) {}
    
    // 插入带权重的单词
    void insert(const string& word, int weight = 1) {
        auto current = root.get();
        
        for (char c : word) {
            if (current->children.find(c) == current->children.end()) {
                current->children[c] = make_unique<WeightedTrieNode>();
            }
            current = current->children[c].get();
        }
        
        current->isEnd = true;
        current->weight += weight;  // 累加权重
    }
    
    // 获取单词权重
    int getWeight(const string& word) {
        auto node = findNode(word);
        return node && node->isEnd ? node->weight : 0;
    }
    
    // 获取前缀权重(所有以该前缀开头的单词权重之和)
    int getPrefixWeight(const string& prefix) {
        auto node = findNode(prefix);
        if (!node) return 0;
        
        return dfsSumWeight(node);
    }
    
private:
    WeightedTrieNode* findNode(const string& str) {
        auto current = root.get();
        for (char c : str) {
            if (current->children.find(c) == current->children.end()) {
                return nullptr;
            }
            current = current->children[c].get();
        }
        return current;
    }
    
    int dfsSumWeight(WeightedTrieNode* node) {
        int sum = node->isEnd ? node->weight : 0;
        for (auto& pair : node->children) {
            sum += dfsSumWeight(pair.second.get());
        }
        return sum;
    }
};

3.2 支持删除操作

class DeletableTrie {
private:
    struct TrieNode {
        bool isEnd;
        unordered_map<char, unique_ptr<TrieNode>> children;
        
        TrieNode() : isEnd(false) {}
    };
    
    unique_ptr<TrieNode> root;
    
public:
    DeletableTrie() : root(make_unique<TrieNode>()) {}
    
    void insert(const string& word) {
        auto current = root.get();
        for (char c : word) {
            if (current->children.find(c) == current->children.end()) {
                current->children[c] = make_unique<TrieNode>();
            }
            current = current->children[c].get();
        }
        current->isEnd = true;
    }
    
    bool search(const string& word) {
        auto node = findNode(word);
        return node && node->isEnd;
    }
    
    // 删除操作
    bool remove(const string& word) {
        return removeHelper(root.get(), word, 0);
    }
    
private:
    TrieNode* findNode(const string& str) {
        auto current = root.get();
        for (char c : str) {
            if (current->children.find(c) == current->children.end()) {
                return nullptr;
            }
            current = current->children[c].get();
        }
        return current;
    }
    
    // 递归删除辅助函数
    bool removeHelper(TrieNode* node, const string& word, int depth) {
        if (depth == word.length()) {
            // 到达字符串末尾
            if (!node->isEnd) {
                return false;  // 单词不存在
            }
            node->isEnd = false;
            // 如果没有子节点,可以删除这个节点
            return node->children.empty();
        }
        
        char c = word[depth];
        auto it = node->children.find(c);
        if (it == node->children.end()) {
            return false;  // 路径不存在
        }
        
        // 递归删除
        bool shouldDeleteChild = removeHelper(it->second.get(), word, depth + 1);
        
        if (shouldDeleteChild) {
            node->children.erase(it);
            // 如果当前节点不是单词结尾且没有其他子节点,也应该被删除
            return !node->isEnd && node->children.empty();
        }
        
        return false;
    }
};

3.3 支持通配符搜索

class WildcardTrie {
private:
    struct TrieNode {
        bool isEnd;
        unordered_map<char, unique_ptr<TrieNode>> children;
        
        TrieNode() : isEnd(false) {}
    };
    
    unique_ptr<TrieNode> root;
    
public:
    WildcardTrie() : root(make_unique<TrieNode>()) {}
    
    void insert(const string& word) {
        auto current = root.get();
        for (char c : word) {
            if (current->children.find(c) == current->children.end()) {
                current->children[c] = make_unique<TrieNode>();
            }
            current = current->children[c].get();
        }
        current->isEnd = true;
    }
    
    // 支持通配符 '.' 的搜索
    bool searchWithWildcard(const string& word) {
        return searchHelper(root.get(), word, 0);
    }
    
private:
    bool searchHelper(TrieNode* node, const string& word, int depth) {
        if (depth == word.length()) {
            return node->isEnd;
        }
        
        char c = word[depth];
        
        if (c == '.') {
            // 通配符匹配任意字符
            for (auto& pair : node->children) {
                if (searchHelper(pair.second.get(), word, depth + 1)) {
                    return true;
                }
            }
            return false;
        } else {
            // 普通字符匹配
            auto it = node->children.find(c);
            if (it == node->children.end()) {
                return false;
            }
            return searchHelper(it->second.get(), word, depth + 1);
        }
    }
};

4. 性能优化技术

4.1 内存池优化

class TrieMemoryPool {
private:
    struct TrieNode {
        bool isEnd;
        unordered_map<char, TrieNode*> children;
        
        TrieNode() : isEnd(false) {}
    };
    
    // 内存池
    vector<unique_ptr<TrieNode[]>> memoryBlocks;
    vector<TrieNode*> freeList;
    static const size_t BLOCK_SIZE = 1000;
    
    TrieNode* allocateNode() {
        if (freeList.empty()) {
            // 分配新块
            auto block = make_unique<TrieNode[]>(BLOCK_SIZE);
            memoryBlocks.push_back(move(block));
            
            // 将新块中的所有节点加入空闲列表
            for (size_t i = 0; i < BLOCK_SIZE; i++) {
                freeList.push_back(&memoryBlocks.back()[i]);
            }
        }
        
        TrieNode* node = freeList.back();
        freeList.pop_back();
        new(node) TrieNode(); // 调用构造函数
        return node;
    }
    
    void deallocateNode(TrieNode* node) {
        node->~TrieNode(); // 调用析构函数
        freeList.push_back(node);
    }
    
    unique_ptr<TrieNode> root;
    
public:
    TrieMemoryPool() {
        root = unique_ptr<TrieNode>(allocateNode());
    }
    
    void insert(const string& word) {
        TrieNode* current = root.get();
        
        for (char c : word) {
            if (current->children.find(c) == current->children.end()) {
                current->children[c] = allocateNode();
            }
            current = current->children[c];
        }
        
        current->isEnd = true;
    }
    
    // ... 其他方法类似
};

4.2 压缩字典树 (Patricia Trie)

class PatriciaTrieNode {
public:
    string edge;  // 边上的字符串
    bool isEnd;
    unordered_map<char, unique_ptr<PatriciaTrieNode>> children;
    
    PatriciaTrieNode(const string& e = "") : edge(e), isEnd(false) {}
};

class PatriciaTrie {
private:
    unique_ptr<PatriciaTrieNode> root;
    
    // 找到最长公共前缀
    int longestCommonPrefix(const string& s1, const string& s2) {
        int len = min(s1.length(), s2.length());
        for (int i = 0; i < len; i++) {
            if (s1[i] != s2[i]) {
                return i;
            }
        }
        return len;
    }
    
public:
    PatriciaTrie() : root(make_unique<PatriciaTrieNode>()) {}
    
    void insert(const string& word) {
        insertHelper(root.get(), word);
    }
    
private:
    void insertHelper(PatriciaTrieNode* node, const string& word) {
        if (word.empty()) {
            node->isEnd = true;
            return;
        }
        
        char firstChar = word[0];
        auto it = node->children.find(firstChar);
        
        if (it == node->children.end()) {
            // 没有以该字符开头的边,直接添加
            auto newNode = make_unique<PatriciaTrieNode>(word);
            newNode->isEnd = true;
            node->children[firstChar] = move(newNode);
            return;
        }
        
        PatriciaTrieNode* child = it->second.get();
        int lcp = longestCommonPrefix(word, child->edge);
        
        if (lcp == child->edge.length()) {
            // word包含child->edge,继续递归
            insertHelper(child, word.substr(lcp));
            return;
        }
        
        if (lcp == word.length()) {
            // child->edge包含word,需要分割
            string remaining = child->edge.substr(lcp);
            child->edge = word;
            
            auto newChild = make_unique<PatriciaTrieNode>(remaining);
            newChild->children = move(child->children);
            newChild->isEnd = child->isEnd;
            
            child->children.clear();
            child->children[remaining[0]] = move(newChild);
            child->isEnd = true;
            return;
        }
        
        // 部分匹配,需要分割节点
        string common = child->edge.substr(0, lcp);
        string remainingWord = word.substr(lcp);
        string remainingEdge = child->edge.substr(lcp);
        
        child->edge = common;
        
        auto newChild1 = make_unique<PatriciaTrieNode>(remainingEdge);
        newChild1->children = move(child->children);
        newChild1->isEnd = child->isEnd;
        
        auto newChild2 = make_unique<PatriciaTrieNode>(remainingWord);
        newChild2->isEnd = true;
        
        child->children.clear();
        child->children[remainingEdge[0]] = move(newChild1);
        child->children[remainingWord[0]] = move(newChild2);
        child->isEnd = false;
    }
};

5. 实际应用场景

5.1 拼写检查器

class SpellChecker {
private:
    Trie<TrieNodeMap> dictionary;
    unordered_set<string> suggestions;
    
    // 生成可能的拼写错误
    vector<string> generateEdits(const string& word) {
        vector<string> edits;
        
        // 删除操作
        for (int i = 0; i < word.length(); i++) {
            edits.push_back(word.substr(0, i) + word.substr(i + 1));
        }
        
        // 交换操作
        for (int i = 0; i < word.length() - 1; i++) {
            string swapped = word;
            swap(swapped[i], swapped[i + 1]);
            edits.push_back(swapped);
        }
        
        // 替换操作
        for (int i = 0; i < word.length(); i++) {
            for (char c = 'a'; c <= 'z'; c++) {
                if (c != word[i]) {
                    edits.push_back(word.substr(0, i) + c + word.substr(i + 1));
                }
            }
        }
        
        // 插入操作
        for (int i = 0; i <= word.length(); i++) {
            for (char c = 'a'; c <= 'z'; c++) {
                edits.push_back(word.substr(0, i) + c + word.substr(i));
            }
        }
        
        return edits;
    }
    
public:
    void addWord(const string& word) {
        dictionary.insert(word);
    }
    
    bool check(const string& word) {
        return dictionary.search(word);
    }
    
    vector<string> suggest(const string& word) {
        suggestions.clear();
        
        // 直接搜索
        if (dictionary.search(word)) {
            return {word};
        }
        
        // 生成编辑距离为1的单词
        auto edits = generateEdits(word);
        for (const string& edit : edits) {
            if (dictionary.search(edit)) {
                suggestions.insert(edit);
            }
        }
        
        // 如果没有找到,尝试编辑距离为2
        if (suggestions.empty()) {
            for (const string& edit1 : edits) {
                auto edits2 = generateEdits(edit1);
                for (const string& edit2 : edits2) {
                    if (dictionary.search(edit2)) {
                        suggestions.insert(edit2);
                    }
                }
            }
        }
        
        return vector<string>(suggestions.begin(), suggestions.end());
    }
};

5.2 自动补全系统

class AutoComplete {
private:
    Trie<TrieNodeMap> trie;
    priority_queue<pair<int, string>> topSuggestions;
    
public:
    void addWord(const string& word, int frequency = 1) {
        trie.insert(word);
        // 这里可以维护一个频率映射
    }
    
    vector<string> getSuggestions(const string& prefix, int limit = 5) {
        auto words = trie.getWordsWithPrefix(prefix);
        
        // 按长度排序(短的优先)
        sort(words.begin(), words.end(), [](const string& a, const string& b) {
            if (a.length() != b.length()) {
                return a.length() < b.length();
            }
            return a < b; // 字典序
        });
        
        // 限制返回数量
        if (words.size() > limit) {
            words.resize(limit);
        }
        
        return words;
    }
    
    // 支持模糊匹配的自动补全
    vector<string> getFuzzySuggestions(const string& input, int maxDistance = 1) {
        vector<string> result;
        auto allWords = trie.getWordsWithPrefix("");
        
        for (const string& word : allWords) {
            if (editDistance(input, word) <= maxDistance) {
                result.push_back(word);
            }
        }
        
        // 按编辑距离排序
        sort(result.begin(), result.end(), [&input](const string& a, const string& b) {
            int distA = editDistance(input, a);
            int distB = editDistance(input, b);
            if (distA != distB) {
                return distA < distB;
            }
            return a.length() < b.length();
        });
        
        return result;
    }
    
private:
    int editDistance(const string& s1, const string& s2) {
        int m = s1.length(), n = s2.length();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        
        for (int i = 0; i <= m; i++) dp[i][0] = i;
        for (int j = 0; j <= n; j++) dp[0][j] = j;
        
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (s1[i-1] == s2[j-1]) {
                    dp[i][j] = dp[i-1][j-1];
                } else {
                    dp[i][j] = 1 + min({dp[i-1][j], dp[i][j-1], dp[i-1][j-1]});
                }
            }
        }
        
        return dp[m][n];
    }
};

6. 完整的测试框架

void runTests() {
    cout << "=== Trie 测试开始 ===" << endl;
    
    // 测试基本功能
    Trie<TrieNodeMap> trie;
    
    // 插入测试
    vector<string> words = {"apple", "app", "application", "apply", "banana", "band"};
    for (const string& word : words) {
        trie.insert(word);
        cout << "插入: " << word << endl;
    }
    
    // 搜索测试
    cout << "\n搜索测试:" << endl;
    cout << "app: " << (trie.search("app") ? "找到" : "未找到") << endl;
    cout << "apple: " << (trie.search("apple") ? "找到" : "未找到") << endl;
    cout << "appl: " << (trie.search("appl") ? "找到" : "未找到") << endl;
    cout << "band: " << (trie.search("band") ? "找到" : "未找到") << endl;
    
    // 前缀测试
    cout << "\n前缀测试:" << endl;
    cout << "app: " << (trie.startsWith("app") ? "存在" : "不存在") << endl;
    cout << "ban: " << (trie.startsWith("ban") ? "存在" : "不存在") << endl;
    cout << "cat: " << (trie.startsWith("cat") ? "存在" : "不存在") << endl;
    
    // 自动补全测试
    cout << "\n自动补全测试 (app):" << endl;
    auto suggestions = trie.getWordsWithPrefix("app");
    for (const string& word : suggestions) {
        cout << "  " << word << endl;
    }
    
    // 删除测试
    cout << "\n删除测试:" << endl;
    DeletableTrie delTrie;
    delTrie.insert("hello");
    delTrie.insert("help");
    cout << "删除前 hello: " << (delTrie.search("hello") ? "存在" : "不存在") << endl;
    
    delTrie.remove("hello");
    cout << "删除后 hello: " << (delTrie.search("hello") ? "存在" : "不存在") << endl;
    cout << "help 仍然存在: " << (delTrie.search("help") ? "存在" : "不存在") << endl;
    
    // 通配符搜索测试
    cout << "\n通配符搜索测试:" << endl;
    WildcardTrie wcTrie;
    wcTrie.insert("hello");
    wcTrie.insert("help");
    wcTrie.insert("held");
    
    cout << "搜索 h.llo: " << (wcTrie.searchWithWildcard("h.llo") ? "找到" : "未找到") << endl;
    cout << "搜索 hel.: " << (wcTrie.searchWithWildcard("hel.") ? "找到" : "未找到") << endl;
    cout << "搜索 h...o: " << (wcTrie.searchWithWildcard("h...o") ? "找到" : "未找到") << endl;
    
    cout << "=== Trie 测试结束 ===" << endl;
}

int main() {
    runTests();
    
    // 演示实际应用
    cout << "\n=== 实际应用演示 ===" << endl;
    
    // 拼写检查器演示
    SpellChecker checker;
    checker.addWord("apple");
    checker.addWord("apply");
    checker.addWord("app");
    
    cout << "拼写检查 'aple': ";
    auto suggestions = checker.suggest("aple");
    for (const string& sug : suggestions) {
        cout << sug << " ";
    }
    cout << endl;
    
    // 自动补全演示
    AutoComplete ac;
    ac.addWord("programming");
    ac.addWord("program");
    ac.addWord("programmer");
    ac.addWord("progress");
    
    cout << "自动补全 'prog': ";
    auto acSuggestions = ac.getSuggestions("prog");
    for (const string& sug : acSuggestions) {
        cout << sug << " ";
    }
    cout << endl;
    
    return 0;
}

7. 复杂度深入分析

7.1 时间复杂度

操作最好情况平均情况最坏情况
插入O(m)O(m)O(m)
搜索O(m)O(m)O(m)
前缀搜索O(m)O(m)O(m)
删除O(m)O(m)O(m)
自动补全O(m + k)O(m + k)O(m + k)

其中:

  • m:字符串长度
  • k:匹配的单词数量

7.2 空间复杂度

实现方式空间复杂度说明
数组实现O(σ×N)σ为字符集大小,N为节点数
哈希表实现O(N)N为节点数,常数因子较小
向量实现O(N)空间效率介于数组和哈希表之间
压缩字典树O(N)节点数显著减少

7.3 性能对比

// 性能测试代码
void performanceTest() {
    const int TEST_SIZE = 10000;
    vector<string> testWords;
    
    // 生成测试数据
    for (int i = 0; i < TEST_SIZE; i++) {
        string word = "word" + to_string(i);
        testWords.push_back(word);
    }
    
    // 测试Trie插入性能
    auto start = chrono::high_resolution_clock::now();
    Trie<TrieNodeMap> trie;
    for (const string& word : testWords) {
        trie.insert(word);
    }
    auto end = chrono::high_resolution_clock::now();
    auto trieInsertTime = chrono::duration_cast<chrono::microseconds>(end - start);
    
    // 测试搜索性能
    start = chrono::high_resolution_clock::now();
    for (const string& word : testWords) {
        trie.search(word);
    }
    end = chrono::high_resolution_clock::now();
    auto trieSearchTime = chrono::duration_cast<chrono::microseconds>(end - start);
    
    cout << "Trie插入 " << TEST_SIZE << " 个单词: " << trieInsertTime.count() << " 微秒" << endl;
    cout << "Trie搜索 " << TEST_SIZE << " 个单词: " << trieSearchTime.count() << " 微秒" << endl;
}

8. 设计模式应用

8.1 工厂模式创建不同类型的Trie

enum class TrieType {
    ARRAY,
    HASHMAP,
    VECTOR,
    SMART_POINTER
};

class TrieFactory {
public:
    template<typename T>
    static unique_ptr<Trie<T>> createTrie() {
        return make_unique<Trie<T>>();
    }
    
    static unique_ptr<Trie<TrieNodeMap>> createDefaultTrie() {
        return createTrie<TrieNodeMap>();
    }
    
    static unique_ptr<Trie<TrieNodeArray>> createArrayTrie() {
        return createTrie<TrieNodeArray>();
    }
};

8.2 观察者模式(监控Trie变化)

class TrieObserver {
public:
    virtual void onInsert(const string& word) = 0;
    virtual void onRemove(const string& word) = 0;
    virtual ~TrieObserver() = default;
};

class ObservableTrie : public Trie<TrieNodeMap> {
private:
    vector<TrieObserver*> observers;
    
public:
    void addObserver(TrieObserver* observer) {
        observers.push_back(observer);
    }
    
    void removeObserver(TrieObserver* observer) {
        observers.erase(
            remove(observers.begin(), observers.end(), observer),
            observers.end()
        );
    }
    
    void insert(const string& word) override {
        Trie<TrieNodeMap>::insert(word);
        for (auto* observer : observers) {
            observer->onInsert(word);
        }
    }
    
    bool remove(const string& word) {
        bool result = DeletableTrie::remove(word);
        if (result) {
            for (auto* observer : observers) {
                observer->onRemove(word);
            }
        }
        return result;
    }
};

9. 总结

字典树作为一种强大的字符串处理数据结构,具有以下优势:

  1. 高效查询:O(m)时间复杂度,与字典大小无关
  2. 前缀共享:节省存储空间,特别适合有公共前缀的字符串集合
  3. 功能丰富:支持自动补全、拼写检查、通配符搜索等高级功能
  4. 可扩展性强:易于添加权重、删除、监控等特性
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值