字典树(Trie)是处理字符串匹配的高效数据结构,广泛应用于搜索提示、拼写检查等场景。本文将带你从零掌握字典树的原理与实现!

一、什么是字典树?

字典树(Trie)是一种树形数据结构,用于高效存储和检索字符串集合。它的核心优势在于:

  • 前缀共享:具有相同前缀的字符串共享树中的路径

  • 查找高效:查找时间复杂度仅与字符串长度相关(O(L))

  • 自动排序:存储的字符串按字典序自动排列

典型应用场景:

  1. 搜索引擎的自动补全功能

  2. 拼写检查与单词提示

  3. IP路由表的最长前缀匹配

  4. 单词游戏(如Boggle、Scrabble)

二、字典树的核心原理

基本结构

字典树是一棵多叉树,每个节点包含:

  • 指向子节点的指针数组(通常26个,对应26个字母)

  • 结束标记(标识从根到当前节点是否构成完整单词)

工作方式

假设我们存储单词:["cat", "car", "dog"]

结构说明:

根节点 (root)
  • 不存储具体字符

  • 包含指向所有首字母子节点的指针

字符节点
  • 每个节点存储一个字符

  • 包含 26 个子指针(对应 a-z)

  • 示例路径:

    • root → c → a → t 形成 "cat"

    • root → c → a → r 形成 "car"

    • root → d → o → g 形成 "dog"

单词结束标记 (*)
  • 红色节点表示单词结束位置

  • t* 标记 "cat" 结束

  • r* 标记 "car" 结束

  • g* 标记 "dog" 结束

共享前缀机制
  • "cat" 和 "car" 共享 c→a 路径

  • 节省存储空间的关键特性

三、C++实现字典树

节点结构设计

const int ALPHABET_SIZE = 26;class TrieNode {
public:TrieNode* children[ALPHABET_SIZE];bool isEndOfWord; // 标记是否单词结束TrieNode() {isEndOfWord = false;for (int i = 0; i < ALPHABET_SIZE; i++) {children[i] = nullptr;}}
};

字典树类框架

class Trie {
private:TrieNode* root;// 递归删除节点(用于析构)void deleteTrie(TrieNode* node) {if (!node) return;for (int i = 0; i < ALPHABET_SIZE; i++) {if (node->children[i]) {deleteTrie(node->children[i]);}}delete node;}public:Trie() {root = new TrieNode();}~Trie() {deleteTrie(root);}// 插入单词void insert(string word);// 搜索单词(完全匹配)bool search(string word);// 检查前缀是否存在bool startsWith(string prefix);
};

关键操作实现

1. 插入操作
void Trie::insert(string word) {TrieNode* current = root;for (char c : word) {int index = c - 'a';// 如果字符节点不存在则创建if (!current->children[index]) {current->children[index] = new TrieNode();}current = current->children[index];}current->isEndOfWord = true; // 标记单词结束
}
2. 搜索操作
bool Trie::search(string word) {TrieNode* current = root;for (char c : word) {int index = c - 'a';if (!current->children[index]) {return false; // 字符不存在}current = current->children[index];}return current->isEndOfWord; // 检查是否单词结束
}
3. 前缀检查
bool Trie::startsWith(string prefix) {TrieNode* current = root;for (char c : prefix) {int index = c - 'a';if (!current->children[index]) {return false;}current = current->children[index];}return true; // 前缀存在
}

四、复杂度分析

操作

时间复杂度

空间复杂度

插入单词

O(L)

O(L)

搜索单词

O(L)

O(1)

前缀检查

O(L)

O(1)

L = 字符串长度

五、实战应用:自动补全系统

// 获取以指定前缀开头的所有单词
vector<string> getSuggestions(TrieNode* root, string prefix) {vector<string> suggestions;TrieNode* current = root;// 定位到前缀的最后一个节点for (char c : prefix) {int index = c - 'a';if (!current->children[index]) {return suggestions; // 前缀不存在}current = current->children[index];}// DFS收集所有单词collectWords(current, prefix, suggestions);return suggestions;
}// DFS遍历收集单词
void collectWords(TrieNode* node, string currentWord, vector<string>& words) {if (!node) return;if (node->isEndOfWord) {words.push_back(currentWord);}for (int i = 0; i < ALPHABET_SIZE; i++) {if (node->children[i]) {char c = 'a' + i;collectWords(node->children[i], currentWord + c, words);}}
}

六、字典树的优化方向

  1. 内存优化:使用vector动态存储子节点(牺牲时间换空间)

  2. 删除操作:添加引用计数支持安全删除

  3. 压缩字典树:合并只有一个子节点的连续节点

  4. 支持Unicode:使用哈希表替代固定数组

七、总结与思考

字典树的核心价值在于解决字符串前缀匹配问题。相比哈希表:

特性

字典树

哈希表

前缀搜索

高效支持

不支持

内存占用

较高(空间换时间)

较低

查找效率

O(L)

O(1)平均

自动排序

支持

不支持

适用场景选择:

  • 需要前缀匹配 → 字典树

  • 仅需精确匹配 → 哈希表

  • 内存敏感场景 → 三向单词查找树

纸上得来终觉浅,绝知此事要躬行!建议尝试实现字典树后,在LeetCode上练习相关题目(如208, 211, 212题)巩固理解。


扩展思考:如何修改字典树结构使其支持中文?欢迎在评论区分享你的思路!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/87798.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/87798.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/87798.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot整合SpringCache缓存

SpringBoot整合SpringCache使用缓存 文章目录SpringBoot整合SpringCache使用缓存1.介绍2.SpringBoot整合1.导入xml依赖2.配置yml3.使用EnableCaching启用SpringCache4.Cacheable5.CachePut6.CacheEvict7. Caching8.CacheConfig3.其他属性配置1.keyGenerator 属性2. cacheManage…

WPF学习笔记(20)Button与控件模板

Button与控件模板一、 Button默认控件模板详解二、自定义按钮模板一、 Button默认控件模板详解 WPF 中的大多数控件都有默认的控件模板。 这些模板定义了控件的默认外观和行为&#xff0c;包括控件的布局、背景、前景、边框、内容等。 官方文档&#xff1a;https://learn.mic…

蓝天居士自传(1)

蓝天居士何许人&#xff1f; 蓝天居士是我的笔名&#xff0c;也可以说是号。就好像李白号青莲居士、欧阳修号六一居士一样。笔者本名彭昊 —— 一个有不少重名重姓者的名字。 笔者小的时候上语文课&#xff0c;无论是小学、初中抑或是高中&#xff0c;都会有鲁迅&#xff08;…

短剧系统开发定制全流程解析:从需求分析到上线的专业指南

一、短剧行业数字化趋势与系统开发必要性在短视频内容爆发式增长的时代背景下&#xff0c;短剧作为一种新兴的内容形式正在迅速崛起。数据显示&#xff0c;2023年中国短剧市场规模已突破300亿元&#xff0c;用户规模达到4.5亿&#xff0c;年增长率超过200%。这一迅猛发展的市场…

getBoundingClientRect() 详解:精准获取元素位置和尺寸

getBoundingClientRect() 是 JavaScript 中一个强大的 DOM API&#xff0c;用于获取元素在视口中的精确位置和尺寸信息。它返回一个 DOMRect 对象&#xff0c;包含元素的坐标、宽度和高度等关键几何信息。 基本用法 const element document.getElementById(myElement); cons…

EXCEL 基础技巧

来源&#xff1a;WPS 官网 初步了解WPS表格-WPS学堂https://www.wps.cn/learning/course/detail/id/635.html 1、格式刷 1.1使用格式刷隔行填充颜色。 首先设置部分表格颜色&#xff0c;选中此区域&#xff0c;双击点击格式刷&#xff0c;然后选中其他表格区域。 这样就可以…

【RK3568 编译rtl8723DU驱动】

RK3568 编译rtl8723DU驱动 编译源码1.解压rtl8723du2.修改Makefile 验证1.加载模块2.开启wifi 在驱动开发中&#xff0c;驱动的编译与集成是实现设备功能的关键环节。本文聚焦于基于 RK3568 处理器平台编译 RTL8723DU WiFi/BT 二合一模块驱动的完整流程&#xff0c;涵盖源码编译…

基于Simulink的二关节机器人独立PD控制仿真

文章目录 理论模型仿真窗口控制函数目标函数仿真 本文是刘金琨. 机器人控制系统的设计与MATLAB仿真的学习笔记。 理论模型 对于二关节机器人系统&#xff0c;其动力学模型为 D ( q ) q C ( q , q ˙ ) q ˙ r D(q)\ddot qC(q,\dot q)\dot q r D(q)q​C(q,q˙​)q˙​r 式…

【技术架构解析】国产化双复旦微FPGA+飞腾D2000核心板架构

本文就一款基于飞腾D2000核心板与两片高性能FPGA的国产化开发主板进行技术解析&#xff0c;包括系统架构、主要硬件模块、关键接口及软件环境&#xff0c;重点阐述各子系统间的数据路径与协同工作方式&#xff0c;旨在为行业内同类产品设计与应用提供参考。 随着国产化要求的加…

Python 数据分析:计算,分组统计1,df.groupby()。听故事学知识点怎么这么容易?

目录1 示例代码2 欢迎纠错3 论文写作/Python 学习智能体1 示例代码 直接上代码。 def grpby1():xls "book.xls"df pd.DataFrame(pd.read_excel(xls, engine"xlrd"))print(df)"""序号 分类 销量0 1 文学 51 2 计算机…

【解决“此扩展可能损坏”】Edge浏览器(chrome系列通杀))扩展损坏?一招保留数据快速修复

引言 如果你想保留你的数据&#xff0c;敲重点&#xff1a;不要点击修复&#xff0c;不要修复&#xff0c;不要修复 在使用 Microsoft Edge 浏览器时&#xff0c;您可能会遇到扩展程序显示“此扩展程序可能已损坏”的提示&#xff0c;且启用按钮无法点击。这一问题让许多用户感…

AI专业化应用加速落地,安全治理挑战同步凸显

7月2日&#xff0c;2025全球数字经济大会在北京国家会议中心开幕。本届大会以“建设数字友好城市”为主题&#xff0c;聚焦数字技术对城市发展的影响。开幕式上&#xff0c;一首完全由AI生成的MV成为焦点——从歌词、谱曲、演唱到视频制作全流程AI生成&#xff0c;展现人工智能…

Python统一调用多家大模型API指南

随着大模型技术的快速发展&#xff0c;市场上出现了越来越多的LLM服务提供商&#xff0c;包括OpenAI、Anthropic、Google、百度、阿里云等。作为开发者&#xff0c;我们经常需要在不同的模型之间切换&#xff0c;或者同时使用多个模型来满足不同的业务需求。本文将详细介绍如何…

【ESP32】1.编译、烧录、创建工程

标题打开一个Hello world工程并烧录 点击环境搭建链接 遇到的问题&#xff1a; 1.ESP32在VSCODE中烧录代码时&#xff0c;跳出窗口&#xff0c;OPenOCD is not running ,do you want to launch it? 可能是OCD没安装&#xff0c;重新安装 ESP-IDF试一下&#xff0c;在终端命令窗…

调参——optuna

它基于贝叶斯优化&#xff08;Bayesian Optimization&#xff09;思想&#xff0c;通过构建一个概率模型来预测超参数组合的性能&#xff0c;从而高效地探索超参数空间。相比传统网格搜索&#xff08;Grid Search&#xff09;或随机搜索&#xff08;Random Search&#xff09;&…

Redis的缓存击穿和缓存雪崩

Redis缓存击穿和缓存雪崩是两种常见的缓存问题&#xff0c;它们都可能导致系统性能下降甚至崩溃。以下是对它们的详细解释&#xff1a;一、缓存击穿定义缓存击穿是指一个特定的缓存数据失效&#xff08;例如过期&#xff09;&#xff0c;而此时大量请求同时访问这个数据&#x…

Python训练营Day4

浙大疏锦行 Python训练营Day4 内容&#xff0c;pandas处理表格信息&#xff1a; 查看表格统计信息&#xff1a; data.mean()data.mode()data.median() 查看表格信息&#xff1a; data.info()data.describe()data.isnull()data.head() 填充空缺列&#xff1a; 数值型&#xff…

React 基本介绍与项目创建

为什么使用 React 以及前端框架 工作原理 React 通过构建虚拟 DOM&#xff08;Virtual DOM&#xff09;来高效管理界面。当组件的状态或属性发生变化时&#xff0c;React 会重新渲染生成新的虚拟 DOM&#xff0c;并通过 Diff 算法找出新旧虚拟 DOM 树之间的差异&#xff0c;最…

OpenCV CUDA模块设备层-----“小于阈值设为零” 的图像处理函数thresh_to_zero_func()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 OpenCV CUDA 模块&#xff08;cudev&#xff09; 中的一个仿函数生成器&#xff0c;用于创建一个 “小于阈值设为零” 的图像处理函数对象。 这个函…

数字图像处理学习笔记

1-图像处理基础_哔哩哔哩_bilibili 输出图像像素点需要将图象值要作类型转换&#xff0c;转成Int 图像仿射变换 线性变换平移 线性变换&#xff1a; 1&#xff0c;变换前直线&#xff0c;变换后仍然直线 2&#xff0c;直线比例不变 3&#xff0c;直线到远点的距离不变 仿射变…