Trie(字典树/前缀树)

这段代码实现了一个标准且高效的 Trie(字典树/前缀树) 。它的核心逻辑是利用 数组下标 来代表字符,通过 指针跳转 来表示字符的连接。


1. 核心设计思路

  • 公共前缀的共享:如果两个单词有共同的前缀(比如 "apple" 和 "apply"),它们在树中会共用前面的节点 "a"->"p"->"p"->"l",只在最后一位分叉。

  • 字符不存储在节点内 :这是一个巧妙的点。字母 a-z 并不存在变量里,而是隐含在 next[26] 数组的下标 中。如果 next[0] 不为空,就代表存在字符 'a'。

  • 状态标记 :isEnd 就像一个终点路标,标记从根节点走到这里是否构成了一个完整的单词,而不仅仅是某个单词的前缀。


2. 代码逐行注释

C++

cpp 复制代码
class Trie {
private:
    bool isEnd;        // 标记:当前节点是否为一个单词的结束位置
    Trie* next[26];    // 指针数组:存储指向 26 个字母子节点的指针

public:
    // --- 构造函数:初始化节点 ---
    Trie() {
        isEnd = false;               // 初始化为非结尾状态
        memset(next, 0, sizeof(next)); // 将 26 个子节点指针全部置为 NULL (0)
    }
    
    // --- 插入操作:向字典树添加一个单词 ---
    void insert(string word) {
        Trie* node = this;           // 从当前根节点开始遍历
        for (char c : word) {        // 遍历单词的每个字符
            // 如果字符对应的子节点不存在 (NULL)
            if (node->next[c-'a'] == NULL) {
                node->next[c-'a'] = new Trie(); // 创建一个新的子节点
            }
            // 移动指针到对应的子节点上(相当于走到了这个字母的楼层)
            node = node->next[c-'a'];
        }
        // 单词遍历结束后,将最后的节点标记为单词结尾
        node->isEnd = true;
    }
    
    // --- 查找操作:判断一个单词是否完整存在于树中 ---
    bool search(string word) {
        Trie* node = this;           // 从根节点开始
        for (char c : word) {
            node = node->next[c - 'a']; // 沿着字母路径往下跳
            if (node == NULL) {         // 如果中途发现路径断了,说明单词不存在
                return false;
            }
        }
        // 路径走通了,但还要检查该节点是否是一个单词的终点 (isEnd)
        // 比如存了 "apple",搜 "app" 虽然路径通,但 "p" 不是终点,返回 false
        return node->isEnd;
    }
    
    // --- 前缀查找:判断是否有单词以 prefix 开头 ---
    bool startsWith(string prefix) {
        Trie* node = this;
        for (char c : prefix) {
            node = node->next[c-'a']; // 沿着前缀路径往下走
            if (node == NULL) {       // 只要路径中途断开,说明没有以此为开头的单词
                return false;
            }
        }
        // 只要路径能全部走完,就说明前缀存在(不需要判断 isEnd)
        return true;
    }
};

3. 运行逻辑图解

假设我们执行:insert("app"), insert("apple")

  1. 插入 "app":

    • 创建 a -> p -> p 三个节点。

    • 在第二个 p 节点打上标签 isEnd = true。

  2. 插入 "apple":

    • 发现 a, p, p 已存在,直接沿用。

    • 创建新节点 l -> e。

    • 在 e 节点打上标签 isEnd = true。

  3. 查询 "app":

    • 路径走通了,且 isEnd 为 true,返回 true。
  4. 查询 "ap":

    • 路径走通了,但 isEnd 为 false(因为没有存过 "ap" 这个词),search 返回 false,但 startsWith 会返回 true。

4. 复杂度分析

  • 时间复杂度:

    • insert: O(L),其中 L 是单词长度。

    • search: O(L)。

    • startsWith: O(L)。

    • 注:与树中存了多少个单词无关,只跟当前查的单词长度有关,这就是 Trie 强大的原因。

  • 空间复杂度:O(N \\times 26),N 是所有单词的节点总数。每个节点都要预留 26 个指针位,比较耗内存。

  • 前缀树 数据结构 next是一个数组

相关推荐
C^h6 小时前
c++ string
开发语言·c++
raindayinrain6 小时前
c++高性能--原子操作,无锁编程,操作顺序
开发语言·c++·性能优化
学逆向的6 小时前
C++ 合并命名空间
开发语言·c++·网络安全
Zhao-Jichao7 小时前
VS2026 打包 C++ 程序生成 EXE,借助 Installer Project 扩展工具
开发语言·c++
学逆向的7 小时前
C 局部静态变量
c语言·开发语言·网络安全
大都督会赢的8 小时前
C++第一节课
开发语言·c++
“AI国潮设计-小江”8 小时前
【AIGC实战】Python+Stable Diffusion打造“英歌舞财神”IP,解锁“马到成功”等爆款情绪营销文案设计
开发语言·人工智能·python·prompt·aigc
小王C语言8 小时前
Qt常用控件:容器类控件,标题分组框、标签页(浏览器标签)
开发语言·qt
我是唐青枫9 小时前
Kotlin Delegation 委托详解
开发语言·kotlin
qq_406981039 小时前
共享变量一 关于extern与const
开发语言·c++