news 2026/4/3 3:05:30

【字典树 C++ 实现】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【字典树 C++ 实现】

文章目录

  • 前言
  • 一、字典树(Trie)是什么?
  • 二、基本操作与算法思路
    • 1. 插入(Insert)
    • 2. 查找(Search)
    • 3. 前缀判断(StartsWith)
    • 4. 删除(Remove)
    • 5. 自动补全(Autocomplete)
  • 三、C++ 实现
  • 四、测试
  • 五、复杂度分析

前言

字典树(Trie,也叫前缀树)适合用于实现自动补全、前缀搜索、单词字典、敏感词过滤等功能。


一、字典树(Trie)是什么?

Trie 是一棵多叉树(每个结点代表一个字符),从根节点到某个结点的路径表示一个字符串的前缀或整个单词。常见特征:

  • 根节点代表空字符串。
  • 每个边对应一个字符(例如英语小写字母 a–z),结点可以有多个子结点。
  • 结点通常保存“是否是单词结尾”的标记(isEnd)。
  • 查询单词或判断前缀都可以在树上沿字符逐层访问完成,时间复杂度与单词长度成线性关系。

优点:查询、插入、前缀查询时间优秀(O(L)),适合海量字符串前缀操作。缺点:内存消耗可能较大(尤其当字母表大或字符串稀疏时)。


二、基本操作与算法思路

1. 插入(Insert)

从根开始,对单词的每个字符:

  • 若当前结点没有对应子结点则创建。
  • 移动到子结点,处理下一个字符。
    最后标记当前结点为单词结尾。

时间复杂度:对单词长度为 L,插入为 O(L)。

2. 查找(Search)

类似插入,不创建结点,只沿字符查找:

  • 若任一字符对应子结点缺失,则单词不存在。
  • 如果遍历完字符且当前结点 isEnd 为真,则单词存在。

时间复杂度:O(L)。

3. 前缀判断(StartsWith)

沿字符查找,若能走完前缀字符则存在前缀。时间复杂度:O§,P 为前缀长度。

4. 删除(Remove)

删除相对复杂,要保证只删除不再被任何单词使用的结点。常用办法是递归:

  • 递归到单词末尾,取消 isEnd 标记。
  • 如果该结点没有子结点,则返回 true 表示该结点可删除,父结点将其指针置空并继续判断。
  • 否则不可删除,返回 false。

时间复杂度:O(L)。

5. 自动补全(Autocomplete)

先定位到前缀结点,然后对该子树做 DFS/BFS 收集最多 k 个单词或全量单词。时间复杂度:查找前缀 O§ + 遍历匹配单词的复杂度(取决于输出量与深度)。


三、C++ 实现

#include<bits/stdc++.h>usingnamespacestd;/* Trie 实现(26 小写字母) 提供:insert, search, startsWith, remove, autocomplete */classTrieNode{public:boolisEnd;// 子结点指针数组(26)array<TrieNode*,26>children;TrieNode():isEnd(false){children.fill(nullptr);}~TrieNode(){for(autop:children){if(p)deletep;}}};classTrie{private:TrieNode*root;// 删除单词的递归函数,返回是否可以删除当前节点boolremoveHelper(TrieNode*node,conststring&word,intdepth){if(!node)returnfalse;if(depth==(int)word.size()){if(!node->isEnd)returnfalse;// 单词不存在node->isEnd=false;// 如果没有子节点,则可以删除该节点for(autoch:node->children)if(ch)returnfalse;returntrue;}intidx=word[depth]-'a';TrieNode*child=node->children[idx];if(!child)returnfalse;boolshouldDeleteChild=removeHelper(child,word,depth+1);if(shouldDeleteChild){deletechild;node->children[idx]=nullptr;// 判断当前节点是否能被删除:非单词结尾且无任何子节点if(!node->isEnd){for(autoch:node->children)if(ch)returnfalse;returntrue;}else{returnfalse;}}returnfalse;}// 自动补全:从 node 开始 DFS 收集单词voiddfsCollect(TrieNode*node,string&path,vector<string>&out,intlimit){if(!node)return;if((int)out.size()>=limit)return;if(node->isEnd)out.push_back(path);for(inti=0;i<26&&(int)out.size()<limit;++i){if(node->children[i]){path.push_back('a'+i);dfsCollect(node->children[i],path,out,limit);path.pop_back();}}}public:Trie(){root=newTrieNode();}~Trie(){deleteroot;}voidinsert(conststring&word){TrieNode*cur=root;for(charc:word){intidx=c-'a';if(idx<0||idx>=26){// 简化:本实现只支持小写字母,遇到其他字符可以选择跳过或抛错continue;}if(!cur->children[idx])cur->children[idx]=newTrieNode();cur=cur->children[idx];}cur->isEnd=true;}boolsearch(conststring&word)const{TrieNode*cur=root;for(charc:word){intidx=c-'a';if(idx<0||idx>=26)returnfalse;if(!cur->children[idx])returnfalse;cur=cur->children[idx];}returncur->isEnd;}boolstartsWith(conststring&prefix)const{TrieNode*cur=root;for(charc:prefix){intidx=c-'a';if(idx<0||idx>=26)returnfalse;if(!cur->children[idx])returnfalse;cur=cur->children[idx];}returntrue;}voidremove(conststring&word){removeHelper(root,word,0);}// 返回最多 limit 个以 prefix 为前缀的单词(按字典序)vector<string>autocomplete(conststring&prefix,intlimit=10){vector<string>res;TrieNode*cur=root;for(charc:prefix){intidx=c-'a';if(idx<0||idx>=26)returnres;if(!cur->children[idx])returnres;cur=cur->children[idx];}string path=prefix;dfsCollect(cur,path,res,limit);returnres;}};

四、测试

intmain(){Trie trie;vector<string>words={"apple","app","application","apt","banana","band","bandit","bat"};for(auto&w:words)trie.insert(w);// 测试 searchcout<<boolalpha;cout<<"search(\"app\") = "<<trie.search("app")<<"\n";// truecout<<"search(\"apply\") = "<<trie.search("apply")<<"\n";// false// 测试 startsWithcout<<"startsWith(\"ap\") = "<<trie.startsWith("ap")<<"\n";// truecout<<"startsWith(\"ba\") = "<<trie.startsWith("ba")<<"\n";// truecout<<"startsWith(\"cat\") = "<<trie.startsWith("cat")<<"\n";// false// 自动补全autocands=trie.autocomplete("ap",5);cout<<"autocomplete(\"ap\"):\n";for(auto&s:cands)cout<<" "<<s<<"\n";// 删除trie.remove("app");cout<<"after remove(\"app\") search(\"app\") = "<<trie.search("app")<<"\n";// depends: app was word, now falsecout<<"after remove(\"app\") startsWith(\"app\") = "<<trie.startsWith("app")<<"\n";// true (because application, apple)// 删除 "application" 再测试trie.remove("application");cout<<"after remove(\"application\") startsWith(\"app\") = "<<trie.startsWith("app")<<"\n";// still true because "apple"trie.remove("apple");cout<<"after remove(\"apple\") startsWith(\"app\") = "<<trie.startsWith("app")<<"\n";// maybe false if none leftreturn0;}

五、复杂度分析

  • 插入 / 查找 / 前缀判断:对长度为 (L) 的单词为 (O(L))。
    (逐字符访问,最多做 L 次指针查找与数组索引。)

  • 删除:最坏情况也为 (O(L)),因为需要沿路径向下再递归回溯判断删除条件。

  • 空间复杂度:取决于树中结点数量。最坏情况(没有共享前缀)结点数等于所有单词长度之和,即 (\sum_{w\in S} |w|)。每个结点保存 26 个指针(或使用 map/哈希表以节省稀疏树的内存)。

  • 实际工程中可以通过以下方式优化内存:

    1. 把 children 从array<TrieNode*,26>换成vector<pair<char, TrieNode*>>unordered_map<char, TrieNode*>(节省稀疏树内存,但查找成本上升)。
    2. 使用内存池(pool allocator)减少频繁 new/delete 的开销。
    3. 使用压缩字典树(Radix Tree / Patricia Trie)合并只有一个孩子的链,减少结点数。
    4. 如果只处理小写字母且数据量大,使用array+bitset 带来时间优先的实现。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/1 19:22:34

德卡读卡器SDK快速入门指南:轻松掌握读卡器开发工具

德卡读卡器SDK快速入门指南&#xff1a;轻松掌握读卡器开发工具 【免费下载链接】德卡读卡器SDK下载 本仓库提供德卡读卡器T10、D8、D3和T60系列的最新SDK&#xff08;版本1.5&#xff09;下载。该SDK包含最新的DEMO程序&#xff0c;用户可以通过该程序查询读卡器的版本号&…

作者头像 李华
网站建设 2026/3/28 7:37:02

3、文本构建实用指南

文本构建实用指南 1. 文本元素概述 在构建文本时,文本和图形元素的使用至关重要,如章节标题、表格和交叉引用等。呈现这些元素有多种可接受的方式,选择合适的设计、布局和写作风格能让文档独具特色。同时,应遵循一些在已发布的计算机文档中行之有效的风格和写作惯例,确保…

作者头像 李华
网站建设 2026/4/1 2:48:07

Flink CDC TiDB连接器:构建实时数据管道的完整指南

Flink CDC TiDB连接器&#xff1a;构建实时数据管道的完整指南 【免费下载链接】flink-cdc Flink CDC is a streaming data integration tool 项目地址: https://gitcode.com/GitHub_Trending/flin/flink-cdc 在当今数据驱动的时代&#xff0c;企业对于实时数据处理的需…

作者头像 李华
网站建设 2026/4/1 12:51:47

阿里巴巴Wan2.1:让消费级GPU也能玩转高清视频生成

在AI视频生成领域&#xff0c;一个令人振奋的消息正在传播&#xff1a;阿里巴巴开源的Wan2.1模型&#xff0c;首次将720P高清视频生成的门槛降到了消费级GPU水平。这意味着&#xff0c;普通用户用RTX 4060这样的显卡就能在本地运行高质量的文本生成视频任务&#xff0c;不再需要…

作者头像 李华
网站建设 2026/3/30 7:14:55

BootstrapAdmin:10分钟构建企业级.NET权限管理系统,开发效率提升300%

还在为后台权限系统开发耗费数周时间而烦恼吗&#xff1f;从数据库设计到角色配置&#xff0c;从登录认证到菜单权限&#xff0c;一套完整的权限体系往往消耗开发团队30%以上的宝贵时间。现在&#xff0c;基于.NET 8的BootstrapAdmin权限管理框架彻底改变了这一现状——无需编写…

作者头像 李华
网站建设 2026/3/20 18:23:19

代码重构的艺术与实战指南

代码重构艺术的技术文章大纲引言重构的定义及其在软件开发中的重要性重构与代码优化的区别重构的目标&#xff1a;提升可读性、可维护性、扩展性重构的核心原则保持功能不变的前提下优化代码结构小步迭代&#xff0c;频繁验证自动化测试作为安全网常见的代码坏味道重复代码&…

作者头像 李华