算法题解记录-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 自动机、后缀树)的基础。多写几次,你会发现它的逻辑其实非常直观!

相关推荐
2301_800256112 小时前
【人工智能引论期末复习】第3章 搜索求解2 - 对抗搜索
人工智能·算法·深度优先
程序猿阿伟2 小时前
《量子算法开发实战手册:Python全栈能力的落地指南》
python·算法·量子计算
RisunJan2 小时前
Linux命令-iptables(配置防火墙规则的核心工具)
linux·运维·服务器
UpYoung!2 小时前
【Windows 文件系统管理工具】实用工具之XYplorer 完全指南:专业级文件系统管理的终极解决方案
运维·运维开发·实用工具·文件系统管理·办公学习·xyplorer·windows文件管理工具
叁金Coder2 小时前
【CentOS-Stream-9 配置网卡信息】
linux·运维·centos
石像鬼₧魂石2 小时前
139/445 端口(Samba 服务)渗透测试全流程总结与复习
运维·安全·ssh
赵一舟2 小时前
linux下的磁盘清理
linux·运维·服务器
老鼠只爱大米2 小时前
LeetCode算法题详解 438:找到字符串中所有字母异位词
算法·leetcode·双指针·字符串匹配·字母异位词·滑动窗口算法
子洋2 小时前
基于远程开发的大型前端项目实践
运维·前端·后端