一道有趣的面试:Trie 树及其改进

本文探讨了Trie树在解决单词查询问题时的优势,以及其内存占用过多和尾链浪费的问题。提出了通过内存分配策略修改(使用vector替代new操作)和单链压缩来改进Trie树,降低了内存占用并提高了效率。此外,还提及了针对离线需求的双数组Trie树和Trie图在多模式串匹配加速中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

0x00 导言

Trie 树是一种常见的数据结构,用以解决在给定单词在字典中是否存在的问题,而且支持动态的增删词典内容,常见的实现结构如下:

struct node{
    bool is_word ;
    struct node * [26];
};

这里写图片描述
对于任意词典,查找给定单词的效率为O(1),比hash还要快。hash虽然也是O(1),但是hash不能保证没有冲突,即使预先设计了一个良好的 hash 算法,动态修改词典内容也会导致冲突问题。

0x01 一种改进思路

但是 Trie 本身也有不足的地方:

  1. 内存占用较多,每个节点都会开辟26个指针;
  2. 另外Trie 是一颗前缀树,一颗 Trie 树通常会包含很多浪费的尾链,读者可以想象下垂柳的结构。

这里分享一种笔者曾经用过的改进方法:

  1. 内存分配策略修改,原来的实现通常是每次 new 方法生成一个新的节点,这里我们换一个思路,使用vector,借助STL 的内存分配器帮我们管理内存。这种方式有如下好处:一方面简化了内存维护工作;另一方面,vector支持索引访问,可以将指针优化为索引,指针占用的内存将减少一半。
  2. 单链压缩,对于链上每个节点只有一个指针不为空的情况,压缩为一个节点。

新的 Trie 树结构如下:
这里写图片描述

值得注意的是,这里每个节点只有26个int大小,s 复用了第2、3个索引位置。
第0个索引位置进行了多重复用:
1. 如果idx[0] == 0,表示这是一个压缩节点,s = (char *)(&idx[2]) 表示字符串首地址,并表示是一个单词。
2. 如果idx[0] == -1,表示这是一个空索引,隐形的叶子结点。
3. 如果idx[0] == +1,没有意义。
4. 如果idx[0] == +N(+N > 1),表示这是一个非叶子结点且不是一个单词。
5. 如果idx[0] == -N (-N < -1),表示这是一个非叶子结点,但是一个单词。

此时节点定义如下:

struct node{
    int idx [26];
};

这种情况虽然实现起来稍微麻烦些,但也不会太多,最主要的是相对常规做法可以节省不少内存占用。另外同样可以处理动态修改字典的需求。代码就不贴了,有兴趣的读者可以自行尝试。

Trie 改进还有很多优秀的结论,对于离线需求,比如不修改词典,还有一种双数组Trie树,进一步节省内存,而且可以处理中文等其他编码,结合AC自动机可以实现高效率的文章分词。如果将Trie改进为Trie图,还可以进行多模式串匹配加速。今天就不展开了,等另开文章给大家分享。


欢迎大家关注作者公众号,一起进步~
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值