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

相关推荐
BullSmall5 分钟前
Anolis OS 8.10 Docker 部署 SonarQube 9.9 完整教程
运维·docker·容器
AAA@峥12 分钟前
CentOS7 搭建 ELK 企业级日志集群:从部署到日志可视化完整实战
运维·elk·centos
ZKNOW甄知科技19 分钟前
燕千云深度集成飞书:以AI之力,开启无感IT运维体验
大数据·运维·网络·数据库·人工智能·低代码·集成学习
叮咚侠26 分钟前
docker安装的kibana+elasticsearch,突然kibana界面打不开了
运维·jenkins
nVisual28 分钟前
数据中心机柜负载均衡与三相相位监控方案
运维·网络·负载均衡·数据中心布线·综合布线管理软件
国服第二切图仔30 分钟前
02-breakpoint-system
运维·harmonyos
变量未定义~41 分钟前
最小生成树1(Prim模板)、最小生成树2(kruskal模板)、最近公共祖先(模板)
算法
RisunJan44 分钟前
Linux命令-semanage(SELinux 策略管理)
linux·运维·服务器
这就是佬们吗44 分钟前
Python入门⑤-异常处理、文件操作与实战项目
开发语言·数据库·python·算法·pycharm
LONGZETECH1 小时前
新能源汽车充电设备装配与调试仿真教学软件 技术架构与核心实现解析
大数据·算法·3d·unity·架构·汽车