在 C++ 中创建 trie/后缀树时减少内存使用

Posted

技术标签:

【中文标题】在 C++ 中创建 trie/后缀树时减少内存使用【英文标题】:Reducing memory usage while making a trie/suffix tree in C++ 【发布时间】:2013-08-08 20:48:55 【问题描述】:

我正在尝试在 c++ 中进行尝试,现在我的基本数据结构看起来像..

struct node
    int count; no of times this node has been visited.
    struct node* child[ALPHABET_SIZE]; // Let ALPHABET_SIZE be 26

当字符串变大时,会浪费大量分配的内存。就像我们插入 "he" 我们的树将是

root---->h--->e
    |--->e

我们看到在根目录中只有2/26th 分配的内存被使用了。 如何改进??

【问题讨论】:

【参考方案1】:

一些非常基本的建议:

    如果您的分支因子预计较低,请考虑为子项使用数组以外的其他内容。例如,您可以有一个字母到 node* 对的数组,并对它们进行线性或二进制搜索(如果它们是有序的)。您也可以使用某种地图。 如果您不希望计数以百万/十亿为单位,您也可以使用较小的整数大小进行计数。 您可以从基于 arena 的分配器(即对象池)中获取节点,而不是动态分配节点,从而避免通常添加到堆上分配的对象的堆分配开销。

【讨论】:

我在尝试中看到的另一个技巧是拥有两种节点。一个“前缀”,这是他所拥有的,一个“后缀”,它只是“字符串的其余部分”并且没有孩子。 h->e->l 将有两个后缀节点,“p”和“lo”。根据您的工作方式,您可以通过这种方式节省大量空间 @MooingDuck 这是 Patricia trie 的一种特殊情况,其中只有一个子节点的节点(无论位置如何)都被折叠以消除链接开销? @delnan:没听过这个名字,但是是的,基本上就是这样 基于 arena 的分配器的另一个优点是,节点分配在内存中,从而更好地使用 CPU 缓存。 我正在网上搜索相同的内容,这篇博文几乎解决了我的问题..blog.ivank.net/trie-in-as3.html【参考方案2】:

不是为每个节点创建一个固定大小的数组,而是创建一个包含 1 个元素的数组并在插入子节点时调整它的大小(用大小+1 的新数组替换它)。插入会更慢,因此您可以测试并更改调整大小算法(size+1 或 size*2 或 size + size/2),以便在太慢时分配更少。

【讨论】:

【参考方案3】:

使用adjacency list。

我们可以创建节点列表,而不是树。一个节点将是字典,每个都有“当前值”(字母表)和“下一个状态”(子节点的索引列表)。我们可以在节点中添加其他需要的属性。

在您的情况下: 该列表将是 -

["value":"", "next_state":[1 ], "value":"h", "next_state":[2], "value":"e", "next_state":[ ]]

现在说,我们添加“他的”。该列表将更新为:

["value":"", "next_state": [1 ], "value": "h", "next_state": [2 , 3], "value":"e", "next_state":[ ], "value":"i", "next_state":[4], "value":"s", "next_state":[ ] ,]

注意,index-1 中节点的next_state。我们有两个子节点——“e”和“i”。

它非常高效且易于实施。但是,trie 操作会慢很多。

【讨论】:

以上是关于在 C++ 中创建 trie/后缀树时减少内存使用的主要内容,如果未能解决你的问题,请参考以下文章

如何在Python中创建TRIE

如何在 C# 中创建 trie [关闭]

在 OCaml 中创建 char Trie

在内存 C++ 中创建一个进程

在 C++ 中创建一个指向另一个元素的向量

在 SML Alice 中创建中缀/后缀/前缀解析器