【算法与数据结构】字典树(Trie)详解
目录
一,字典树的定义
二,字典树的代码实现
完整代码+详细注释:
测试用例+测试结果:
三,处理其他字符
四,内存优化与扩展
1. 内存优化
2. 扩展功能
五,扩展功能支持通配符匹配
六,相关题目
1,实现前缀树
2,添加与搜索单词 - 数据结构设计
一,字典树的定义
字典树,也叫前缀树或Trie树,是一种树形结构,是一种用于快速检索字符串的数据结构,每个节点代表一个字符,从根节点到某一节点的路径组成一个字符串。主要思想是利用字符串的公共前缀来节省存储空间。
【示例】:
给你n个单词,进行q次查询,每次询问一个单词,让你判断每次查询的字符串是否存在?
我们能想到的是暴力求解,遍历n个字符串,判断是否存在。
对于该问题,查找字符串,就像我们平时查字典的操作。首先确定它的第一个字母,再确定它的第二个字母......,最后就可以找到想要查找的单词。或者这个字符不存在于字典中。
下图是用字典树来存储字符串"abc","abb","bca"。

上图中,红色节点表示:有一个以此节点为终点的单词。
比如在查找字符串"abb"时,每个字符都可以在树中查到,并且最后一个字符b节点为红色,说明该字符串存在与该字典树中。查找字符串"bc"时,每个租房有都可以在树种查到,但最后一个字符c节点不为红色,说明该字符串不存在于该字典树中。
从上面的示例中可以看出,对于字典树的每次查询,时间复杂度都是log级别的,比暴力求解更优。
二,字典树的代码实现
字典树的结构,通常每个节点包含一个指向子节点的指针数组,这里我们实现一个处理小写字母的字典树,每个字符对应的下标为ch-'a',所以数组的大小为26。另外,还需一个标记位来标记是否是单词的结尾。而当前字符保存在哪呢?其实当前节点的字符可以不用保存,因为我们知道下标,加上‘a',就可以拿到该字符了。
我们主要需要实现三种操作:包含插入、搜索和前缀匹配功能,这里我们用两个函数来实现,然后我们用一个二维数组来实现字典树的建树。
完整代码+详细注释:
#include <iostream>
#include <vector>
using namespace std;//定义节点
struct TrieNode
{vector<TrieNode*> children;//存储字节点的指针数组bool isEnd; //标记位TrieNode():children(26, nullptr), isEnd(false){}
};class Trie
{
public:Trie(){root = new TrieNode();}~Trie(){clear(root);}//插入单词void insert(const string& word){TrieNode* node = root;for (char ch : word){//计算索引int index = ch - 'a'; //当前节点为空,当前节点没有存储ch该字母,new一段空间出来if (node->children[index] == nullptr)node->children[index] = new TrieNode();//当前节点不为空,也就是当前节点已经存储ch该字母了,在ch的下面节点继续插入下一个字母node = node->children[index]; }node->isEnd = true;}//搜索单词bool search(const string& word){TrieNode* node = root;for (auto ch : word){//计算索引int index = ch - 'a';//当前索引下为空,该字母不存在,则该字符串不存在if (node->children[index] == nullptr)return false;//当前索引下不为空,该字母存在,继续匹配下一个字母node = node->children[index];}//检查最后一个字母是否是结尾return node->isEnd; }//前缀匹配bool startsWith(const string& prefix){TrieNode* node = root;for (auto& ch : prefix){int index = ch - 'a';if (node->children[index] == nullptr)return false;node = node->children[index];}return true; //不检查结尾状态}
private://递归释放(析构辅助函数)void clear(TrieNode* node){if (node == nullptr) return;for (int i = 0; i < 26; i++){if (node->children[i])clear(node->children[i]);}delete node;}TrieNode* root; //根节点
};
测试用例+测试结果:
int main() {Trie trie;// 插入单词trie.insert("apple");trie.insert("app");trie.insert("banana");// 搜索测试cout << boolalpha;cout << "Search 'apple': " << trie.search("apple") << endl; // truecout << "Search 'app': " << trie.search("app") << endl; // truecout << "Search 'appl': " << trie.search("appl") << endl; // false// 前缀匹配测试cout << "Prefix 'app': " << trie.startsWith("app") << endl; // truecout << "Prefix 'ban': " << trie.startsWith("ban") << endl; // truecout << "Prefix 'cat': " << trie.startsWith("cat") << endl; // falsereturn 0;
}
三,处理其他字符
我们实现的Trie现在只适用于小写字母。
若要支持更多字符(如大写字母,数字,中文等),需要调整字符索引方式:
// 示例:扩展支持大写字母和数字(共62种字符)
int getIndex(char c) {if (c >= 'a' && c <= 'z') return c - 'a'; // 0-25else if (c >= 'A' && c <= 'Z') return 26 + c - 'A'; // 26-51else if (c >= '0' && c <= '9') return 52 + c - '0'; // 52-61else throw invalid_argument("Invalid character");
}
四,内存优化与扩展
1. 内存优化
-
压缩字典树(Radix Tree):合并只有一个子节点的路径,减少节点数量。
-
动态分配子节点:使用
unordered_map或vector替代固定数组,节省空间(适用于稀疏字符集)。
2. 扩展功能
-
词频统计:在节点中添加
count字段,统计单词出现次数。 -
前缀自动补全:遍历前缀后的所有分支,收集完整单词。
-
模糊搜索:支持通配符(如
app*e)或编辑距离匹配。
总结:
字典树通过共享前缀高效存储字符串集合,适合前缀匹配和快速检索场景 。
五,扩展功能支持通配符匹配
对于通配符的情况,通常的处理方法是在搜索时遇到通配符时遍历所有可能的子节点。例如,处理"ap?e"时,在遍历到通配符的位置,需要检查所有子节点,继续匹配剩下的模式。这种方法可能需要递归或队列来管理多个可能的路径。
如下图所示,当遍历到通配符?的位置时,需要遍历所有子节点l和p,然后继续匹配。直到匹配到e结束。

前面我们是通过vector来模拟字典树,这里用哈希表来实现。
那对于一个节点该存什么?一个标记位与前面实现的用途一样,一个哈希表来存字符和子节点的映射关系。通过该字符,可以找到它的所有字节点。
通配符?可以匹配任意单个字符,通配符*可以匹配任意长度字符(包括空)。
代码+注释:
bool wildcardSearch(TrieNode* node, const string& pattern, int pos)
{//访问到最后一个位置,直接返回if (pos == pattern.size())return node->isEnd;char c = pattern[pos];if (c == '?') //匹配任意单个字符{for (auto& pair : node->children){//尝试匹配所有可能的节点if (wildcardSearch(pair.second, pattern, pos + 1));return true;}//走到这,说明*匹配成任何节点都找不到该字符串return false;}else if (c == '*') //匹配任意长度字符 包括空{return wildcardSearch(node, pattern, pos + 1) //跳过*|| wildcardSearch(node, pattern, pos); //继续匹配当前字符}else //其他正常情况{if (node->children.count(c) == 0)return false;return wildcardSearch(node->children[c], pattern, pos+1);}
}
完整代码:
#include <iostream>
#include <unordered_map>
#include <string>
using namespace std;struct TrieNode
{bool isEnd=false;//结尾标志unordered_map<char, TrieNode*> children; //存储子节点
};class FuzzyTrie
{
public:FuzzyTrie(){root = new TrieNode();}~FuzzyTrie(){clear(root);}void insert(const string& word){//插入逻辑与Trie一样TrieNode* node = root;for (auto ch : word){if (node->children[ch] == nullptr)node->children[ch] = new TrieNode();node = node->children[ch];}node->isEnd = true;}//通配符搜索接口bool wildcardMatch(const string& pattern){return wildcardSearch(root, pattern, 0);}
private://通配符匹配辅助函数bool wildcardSearch(TrieNode* node, const string& pattern, int pos){//访问到最后一个位置,直接返回if (pos == pattern.size())return node->isEnd;char c = pattern[pos];if (c == '?') //匹配任意单个字符{for (auto& pair : node->children){//尝试匹配所有可能的节点if (wildcardSearch(pair.second, pattern, pos + 1));return true;}//走到这,说明*匹配成任何节点都找不到该字符串return false;}else if (c == '*') //匹配任意长度字符 包括空{return wildcardSearch(node, pattern, pos + 1) //跳过*|| wildcardSearch(node, pattern, pos); //继续匹配当前字符}else //其他正常情况{if (node->children.count(c) == 0)return false;return wildcardSearch(node->children[c], pattern, pos+1);}}void clear(TrieNode* node){if (node == nullptr)return;for (auto& pair : node->children){clear(pair.second);}delete node;}TrieNode* root;
};
六,相关题目
1,实现前缀树
题目链接:208. 实现 Trie (前缀树) - 力扣(LeetCode)

class Trie {
public:Trie() :children(26),isend(false){}void insert(string word) {Trie* node=this;for(char ch:word){ch-='a';if(node->children[ch]==nullptr)node->children[ch]=new Trie();node=node->children[ch];}node->isend=true;}Trie* searchPrefix(string prefix){Trie* node=this;for(char ch:prefix){ch-='a';if(node->children[ch]==nullptr)return nullptr;node=node->children[ch];}return node;}bool search(string word) {Trie* node=this->searchPrefix(word);return node!=nullptr&&node->isend;}bool startsWith(string prefix) {return searchPrefix(prefix)!=nullptr;}private:vector<Trie*> children;bool isend;
};/*** Your Trie object will be instantiated and called as such:* Trie* obj = new Trie();* obj->insert(word);* bool param_2 = obj->search(word);* bool param_3 = obj->startsWith(prefix);*/
2,添加与搜索单词 - 数据结构设计
题目链接:211. 添加与搜索单词 - 数据结构设计 - 力扣(LeetCode)

struct TrieNode
{vector<TrieNode*> children;bool isend;TrieNode():children(26,nullptr),isend(false){}
};
void insert(const string& s,TrieNode* root)
{TrieNode* node=root;for(char ch:s){ch-='a';if(node->children[ch]==nullptr)node->children[ch]=new TrieNode();node=node->children[ch];}node->isend=true;
}
class WordDictionary {
public:WordDictionary() {trie=new TrieNode();}void addWord(string word) {insert(word,trie);}bool search(string word) {//遇到.匹配任意字符return dfs(word,0,trie);}bool dfs(string& word,int index,TrieNode* node){if(index==word.size())return node->isend;char ch=word[index];if(ch>='a'&&ch<='z'){TrieNode* children=node->children[ch-'a'];if(children!=nullptr&&dfs(word,index+1,children))return true;}else if(ch=='.'){for(int i=0;i<26;i++){TrieNode* child=node->children[i];if(child!=nullptr&&dfs(word,index+1,child))return true;}}return false;}private:TrieNode* trie;
};
相关文章:
【算法与数据结构】字典树(Trie)详解
目录 一,字典树的定义 二,字典树的代码实现 完整代码详细注释: 测试用例测试结果: 三,处理其他字符 四,内存优化与扩展 1. 内存优化 2. 扩展功能 五,扩展功能支持通配符匹配 六&…...
【JavaEE进阶】MyBatis通过注解实现增删改查
目录 🍃前言 🍀打印日志 🌴传递参数 🎋增(Insert) 🚩返回主键 🎄删(Delete) 🌲改(Update) 🌳查(Select) 🚩起别名 🚩结果映射 🚩开启驼…...
Deepseek 怼CHATGPT实况
提示词: 用犀利的语言批评ChatGPT 深度思考(R1) 嗯,用户让我用犀利的语言批评ChatGPT。首先,我需要理解他们的动机。可能他们遇到了ChatGPT的某些问题,比如回答不准确或者缺乏深度,所以想表达不满。也有…...
【RK3588嵌入式图形编程】-SDL2-构建模块化UI
构建模块化UI 文章目录 构建模块化UI1、概述2、创建UI管理器3、嵌套组件4、继承5、多态子组件6、总结在本文中,将介绍如何使用C++和SDL创建一个灵活且可扩展的UI系统,重点关注组件层次结构和多态性。 1、概述 在前面的文章中,我们介绍了应用程序循环和事件循环,这为我们的…...
Simulink Ststeflow教程 — 2 创建和编辑状态
目录 2.1 创建和编辑状态 2.1.1 状态的创建 2.1.2 创建连接节点 2.1.3 转移 2.1.5 默认转移 在Stateflow模型中,将包含有状态的Stateflow框图称状态图而将不包含任何状态的Stateflow框图称为流程图。其中,状态图是Stateflow最常用的一种形式&#x…...
Fiddler笔记
文章目录 一、与F12对比二、核心作用三、原理四、配置1.Rules:2.配置证书抓取https包3.设置过滤器4、抓取App包 五、模拟弱网测试六、调试1.线上调试2.断点调试 七、理论1.四要素2.如何定位前后端bug 注 一、与F12对比 相同点: 都可以对http和https请求进行抓包分析…...
线上就医全流程医药机构接入文档接口代码-医保就医接口php-demo版本
2025年2月18日11:28:03 国密算法开发库推荐 lpilp/guomi 我测试过php 7.2 - 8.0都可以兼容,如果有能力可以自己开发 目前已经开发了核心的接口的测试demo,并且封装了工具类直接写业务逻辑即可,并且已经有线上项目在使用,如果需要demo代码可…...
Linux升级Anacodna并配置jupyterLab
在使用 Anaconda 的过程中,随着项目和需求的发展,可能需要升级 Anaconda 的 Base 环境中的 Python 版本。本文将详细介绍如何安全地进行升级,包括步骤、代码示例与最终流程图。 升级 Python 一、环境准备 在进行任何升级之前,建…...
com.typesafe.config
com.typesafe.config 是 Typesafe Config 库的核心包,主要用于 统一、灵活地管理应用程序配置,支持从多种格式(如 HOCON、JSON、Java Properties)加载配置,并提供类型安全的访问接口。以下是其核心功能的详细解析&…...
Recall(召回率)和 Precision(精确率) 的区别和F1分数
Recall与Precision 的区别 一 、概念 Recall(召回率)和 Precision(精确率)是信息检索、数据挖掘以及机器学习等领域中评估模型或算法性能的两个重要指标,特别是在分类问题中。 Precision(精确率ÿ…...
智能选路+NAT实验 作业
拓扑图 配置ip 防火墙安全区域划分 用户配置 dns透明...
Vue 项目登录的基本流程
Vue 用户登录的基本流程包括以下6个步骤: 步骤: 1. 创建登录表单 在前端,首先要创建一个登录表单,用户输入账号(用户名、邮箱、手机号等)和密码。 示例:Login.vue <template><div…...
LLM:RAG
原文链接:LLM:RAG 1、RAG 概览 RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合了信息检索(IR)和 LLM 的技术。它的核心思想是在 LLM 生成回答之前,通过检索相关文档…...
基于Qt 和微信小程序的用户管理系统:WebSocket + SQLite 实现注册与登录
目录 一. 概要 二. 技术栈 三. 系统功能设计 3.1 功能模块 3.2 数据表设计 四. 具体实现 4.1 Qt 服务端 4.1.1 初始化 WebSocket 服务器 4.1.2 用户管理界面 4.2 微信小程序端 4.2.1 注册功能 4.2.2 登录功能 五. 运行效果 六. 源码下载 一. 概要 在物联网和智能设备…...
43.日常算法
1.LCR 142. 训练计划 IV 题目来源 给定两个以 有序链表 形式记录的训练计划 l1、l2,分别记录了两套核心肌群训练项目编号,请合并这两个训练计划,按训练项目编号 升序 记录于链表并返回。 注意:新链表是通过拼接给定的两个链表的…...
策略+适配器模式详解
文章目录 1.策略模式1.目录结构2.Strategy.java 策略接口3.StrategyA.java 策略A4.StrategyB.java 策略B5.StrategyC.java 策略C6.Context.java 策略上下文7.Client.java 客户端8.小结 2.适配器模式1.目录结构2.CustomPaymentProcessor.java 自己的支付接口3.PayPalPaymentServ…...
2024年职高单招或高考计算机类投档线
问题: 这些学校2024年职高单招或高考计算机类投档线分别是多少 回答: 部分学校2024年职高单招或高考计算机类投档线如下: 湖南工业职业技术学院 职高单招:未查询到2024年职高单招计算机类专业明确的录取分数线信息。但在2024年…...
Salesforce 检索Layout的设定
做了许多Object,却想不起来怎么设置我的Listview的项目了。 問題: salesforce 最近参照したオブジェクト 表示項目を変更したいですが、「検索レイアウト」の選択メニューが該当オブジェクトのオブジェクトマネージャーから出てないです。 解決方法&am…...
未来游戏:当人工智能重构虚拟世界的底层逻辑
未来游戏:当人工智能重构虚拟世界的底层逻辑 在《赛博朋克2077》夜之城的霓虹灯下,玩家或许已经注意到酒吧里NPC开始出现微表情变化;在《艾尔登法环》的开放世界中,敌人的战术包抄逐渐显露出类人智慧。这些细节预示着游戏产业正站…...
Zabbix——Rocky9安装zabbix相关步骤记录
安装Zabbix 安装MariaDB 这里用MariaDB演示 https://mariadb.org/download/?trepo-config&dRedHatEnterpriseLinux9&v10.11&r_mneusoft 通过这个网址获得连接 选择对应的repo 根据系统版本和要安装的版本选择对应的repo 安装 新建一个repo文件,例…...
SECS/GEM300应用案例参考
GEM300 是一种用于半导体制造领域的通信协议标准,主要用于支持 300mm 晶圆制造的自动化生产。以下是 GEM300 的一些具体应用案例: 1. 半导体设备集成 设备制造商的应用:广州金南瓜科技有限公司通过 GEM300 SDK,帮助国内多个半导体…...
startai产品精修教程
1.把产品放置ps画布中 打开startai插件选择产品精修功能,选择金属材质即可哦 调节一下参数就可以啦,最终效果图 下载地址:StartAI画图软件官网_PS插件StartAI绘画软件生成器_Photoshop图像处理插件...
【YOLOv8】
文章目录 1、yolov8 介绍2、创新点3、模型结构设计3.1、backbone3.2、head 4、正负样本匹配策略5、Loss6、Data Augmentation7、训练、推理8、分割 Demo附录——V1~V8附录——相关应用参考 1、yolov8 介绍 YOLOv8 是 ultralytics 公司在 2023 年 1 月 10 号开源的 YOLOv5 的下…...
【保姆级教程】DeepSeek R1+RAG,基于开源三件套10分钟构建本地AI知识库
一、总体方案 目前在使用 DeepSeek 在线环境时,页面经常显示“服务器繁忙,请稍后再试”,以 DeepSeek R1 现在的火爆程度,这个状况可能还会持续一段时间,所以这里给大家提供了 DeepSeek R1 RAG 的本地部署方案。最后实现…...
Oracle视图(基本使用)
视图 视图是通过定制的方式显示一个或者多个表的数据。 视图可以视为“虚拟表”或“存储的查询”。 视图的优点: 提供了另外一种级别的表安全性隐藏了数据的复杂性简化了用户的SQL命令隔离基表结构的改变通过重命名列,从另一个角度提供数据。 视图里…...
梁文锋亲自挂名DeepSeek发布新论文
由 DeepSeek 联合创始人梁文锋亲自挂名的研究团队,在 arXiv 上发表了一篇题为“Native Sparse Attention: Hardware-Aligned and Natively Trainable Sparse Attention”的论文,提出了一种全新的注意力机制架构 NSA(Native Sparse Attention&…...
低代码(Low Code)全解析:从概念到应用,从选择到价值
在数字化浪潮席卷全球的当下,企业对软件开发的效率与灵活性愈发重视,低代码平台应运而生并迅速掀起技术热潮。 本文基于笔者 6 年的低代码实践经验,深入剖析低代码的诸多方面,涵盖其定义、发展历程、国内平台对比、开发流程、与…...
C++--STL库-List
目录 1.list 的基本使用 1.1 创建和初始化 1.2. 插入元素 1.3. 删除元素 1.4. 访问元素 1.5 遍历 1.6 总结 list是C标准库(STL)中的双向链表容器,属于<list>头文件。 它的特点是: 动态大小:可以随时插入…...
尚硅谷 java 学习Day19 抽象类与抽象方法、接口、内部类
6-5 抽象类(abstract)与抽象方法(important) 一、什么叫抽象类: 有时候将一个父类设计的非常抽象,以至于它没有具体的实例,这样的类称为抽象类 abstract关键字的使用: 1、abstract:抽象的 2、abs…...
HomeAssistant 发现MQTT设备(温度,湿度,开关)
要通过 MQTT 将温度、湿度数据以及一个灯的开关状态传输到 Home Assistant 并实现设备自动发现,可以按照以下步骤操作: 1.前期准备工作 安装MQTT服务器(EMQX)配置好(可以在HA加载项中安装,也可以在NAS上Docker安装) HA的集成中安装MQTT,并且连接上(EM…...
