算法题解记录-208实现Trie前缀树

【前缀树 (Trie)】详解与实现

一、问题理解

前缀树 (Trie) 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。它的核心特点是:

  • 每个节点表示一个字符
  • 从根节点到某一节点的路径上的字符连接起来,构成该节点对应的字符串
  • 常用于自动补全、拼写检查、字典查询等场景

本题要求实现一个 Trie 类,具备以下三个核心功能:

  1. insert(String word) → 插入一个单词
  2. search(String word) → 查找一个完整的单词是否存在
  3. startsWith(String prefix) → 查找是否存在以某个前缀开头的单词

二、数据结构设计

前缀树的核心在于节点设计。每个节点需要:

  1. 一个布尔标记 isEnd:表示从根节点到当前节点的路径是否构成一个完整的单词。
  2. 一个子节点数组 children:用于存储下一个可能出现的字符(通常长度为 26,对应 26 个小写英文字母)。
java 复制代码
class TrieNode {
    boolean isEnd;
    TrieNode[] children;
    
    public TrieNode() {
        isEnd = false;
        children = new TrieNode[26]; // 默认每个元素为 null
    }
}

初始化时

  • 创建根节点 root,它是一个虚拟节点,不存储实际字符。
  • isEnd 初始为 false
  • children 数组初始化为 null 数组,表示还没有任何子节点。

三、方法实现详解

1. 插入操作 insert(String word)

插入一个单词时,从根节点出发,依次处理每个字符:

  1. 计算字符在 children 数组中的索引:index = ch - 'a'
  2. 如果当前节点没有对应的子节点,则新建一个节点
  3. 移动到子节点,继续处理下一个字符
  4. 处理完最后一个字符后,将当前节点的 isEnd 标记为 true
java 复制代码
public void insert(String word) {
    TrieNode node = root;
    for (char ch : word.toCharArray()) {
        int index = ch - 'a';
        if (node.children[index] == null) {
            node.children[index] = new TrieNode();
        }
        node = node.children[index];
    }
    node.isEnd = true;
}

2. 查找完整单词 search(String word)

查找一个完整单词的过程与插入类似,但有两个关键区别:

  1. 如果路径中某个字符对应的子节点不存在,直接返回 false
  2. 遍历到最后一个字符后,需要检查该节点的 isEnd 是否为 true
java 复制代码
public boolean search(String word) {
    TrieNode node = root;
    for (char ch : word.toCharArray()) {
        int index = ch - 'a';
        if (node.children[index] == null) {
            return false;
        }
        node = node.children[index];
    }
    return node.isEnd;
}

3. 查找前缀 startsWith(String prefix)

查找前缀的过程与 search 类似,但不需要检查 isEnd

  • 只要路径上的所有字符都存在对应的子节点,就说明存在以该前缀开头的单词
java 复制代码
public boolean startsWith(String prefix) {
    TrieNode node = root;
    for (char ch : prefix.toCharArray()) {
        int index = ch - 'a';
        if (node.children[index] == null) {
            return false;
        }
        node = node.children[index];
    }
    return true;
}

四、示例演示

假设我们要依次插入以下单词:

  • "apple"
  • "app"

插入过程

复制代码
插入 "apple":
root -> a -> p -> p -> l -> e (isEnd = true)

插入 "app":
root -> a -> p -> p (isEnd = true)

查询过程

  • search("app") → 返回 true(因为 app 是一个完整单词)
  • search("ap") → 返回 false(因为路径存在,但 isEndfalse
  • startsWith("ap") → 返回 true(因为存在以 ap 开头的单词)
  • startsWith("appl") → 返回 true(因为 apple 以此开头)

五、总结与心得

核心思路:

  • 节点设计是关键isEnd 标记 + children 数组
  • 根节点是虚拟节点:不存储字符,只作为起点
  • 插入时建路径:按字符逐层创建或复用节点
  • 查找时走路径 :路径存在 + isEnd 标记决定结果

记忆点:

  1. 节点结构固定:boolean isEnd + TrieNode[26] children
  2. 插入末尾要标记 isEnd = true
  3. 查找完整单词时要检查 isEnd
  4. 查找前缀时只需路径存在即可

为什么归为"图论"?

虽然 Trie 本质是一棵树,但它可以看作一种有向图(节点为状态,边为字符转移)。在算法题分类中,有时将树视为特殊的图,因此将 Trie 归入图论范畴。


六、完整代码模板(Java)

java 复制代码
class Trie {
    class TrieNode {
        boolean isEnd;
        TrieNode[] children;
        public TrieNode() {
            isEnd = false;
            children = new TrieNode[26];
        }
    }
    
    private TrieNode root;
    
    public Trie() {
        root = new TrieNode();
    }
    
    public void insert(String word) {
        TrieNode node = root;
        for (char ch : word.toCharArray()) {
            int index = ch - 'a';
            if (node.children[index] == null) {
                node.children[index] = new TrieNode();
            }
            node = node.children[index];
        }
        node.isEnd = true;
    }
    
    public boolean search(String word) {
        TrieNode node = root;
        for (char ch : word.toCharArray()) {
            int index = ch - 'a';
            if (node.children[index] == null) {
                return false;
            }
            node = node.children[index];
        }
        return node.isEnd;
    }
    
    public boolean startsWith(String prefix) {
        TrieNode node = root;
        for (char ch : prefix.toCharArray()) {
            int index = ch - 'a';
            if (node.children[index] == null) {
                return false;
            }
            node = node.children[index];
        }
        return true;
    }
}

学会了 Trie,你就掌握了一种高效处理字符串集合的工具。它不仅是一个经典的数据结构,更是很多高级算法(如 AC 自动机、后缀树)的基础。多写几次,你会发现它的逻辑其实非常直观!

相关推荐
世人万千丶1 天前
鸿蒙项目实战 - 社区活动编排板:标签云布局算法与自动换行
学习·算法·华为·harmonyos·鸿蒙
zander2581 天前
LeetCode 279. 完全平方数
算法·深度优先
小嵌同学1 天前
Linux 内核中的 Per-CPU 机制详解
linux·运维·arm开发·嵌入式
云飞云共享云桌面1 天前
自动化设备厂 10 人研发团队,如何共用一台服务器做 SolidWorks 建模?
运维·服务器·网络·3d·自动化·制造
筝筝ba1 天前
Configure IP Address Via Different User Roles And Verify(1)
服务器
QH139292318801 天前
NVIDIA H200 H300服务器
运维·服务器·开发语言·网络·信息与通信
xixiaoyunya1 天前
中小微企业轻量组网方案的技术选型与实践:从传统专线到软定义隧道的成本与效能分析
服务器·php·apache
Hello-FPGA1 天前
嵌入式LINUX 设备 ip配置后怎么保存才能掉电不消失?
linux·运维·服务器
典典分享指南1 天前
VS Code Git 工作树:解锁多分支并行开发新体验
算法·决策树·逻辑回归·启发式算法
奥莱维1 天前
KNX酒店方案_KNX专用线与高端酒店技术逻辑
java·服务器·前端·数据库