所以,你的 trie 节点看起来像
struct trie_node {
struct trie_node *next;
strict trie_node *child;
wchar_t character;
off_t position;
};
当然,如果数据始终在内存中,您可以使用size_t position;。
如果我们假设许多前缀没有映射到特定位置(因为它们不是完整的单词),那么为位置使用单独的数组可能会很有用,即。
struct positions {
size_t count_max;
size_t count;
off_t position[];
};
struct trie_node {
struct trie_node *next;
struct trie_node *child;
wchar_t character;
struct positions *position;
};
不对应完整单词的字符节点可以有一个 NULL position 成员。 count_max 对应于分配的位置数,count 对应于当前位置数。必要时可以重新分配数组并调整其大小。这种数组大小调整在实际应用中很常见; (重新分配的)开销被认为是完全可以接受的,尤其是与替代方案相比。
另一个有趣的选择是使用线性数组来按出现的顺序表示文本中的单词,trie 节点中的position 成员指定数组中第一次出现的索引。每个数组条目将包含下一次出现的索引,以及可选的返回 trie 节点的链接:
#include <stdlib.h>
#include <limits.h>
struct trie_node {
struct trie_node *next;
struct trie_node *child;
wchar_t character;
size_t index; /* NO_INDEX if no occurrences */
size_t occurs; /* Num of occurrences, optional */
wchar_t word[]; /* Optional, entire word */
};
/* When 'index' refers to 'none', use: */
#define NO_INDEX SIZE_MAX
struct occurrence {
off_t offset;
size_t next;
struct trie_node *node; /* Optional */
};
一个容器结构会有数组,而 trie 会挂掉它:
struct text {
size_t count_max;
size_t count;
struct occurrence *occurrences;
struct trie_node *trie;
};
然后,您的函数将采用指向 struct text 的指针。
struct text 中的occurrences 数组可以根据需要动态重新分配。 (这也是为什么 trie 节点中的first 成员是数组的索引,而不是指针:如果它是指针,我们可能必须遍历整个 trie 才能更新所有节点的指针,在重新分配数组时,否则。)
请注意,因为我们使用size_t 作为数组的索引,NO_INDEX 是可能的最大值,而size_t 是无符号整数类型,检查if (i < count) 来验证索引就足够了i 有效。
对应一个完整单词的每个 trie 节点都有index != NO_INDEX,C99 灵活数组成员word 初始化为完整单词(包括结尾的L'\0')。 occurs 成员将拥有该词的出现次数,如果它有用。 (没有需要,除了我们人类可能对每个单词的出现次数感兴趣。)
此方案允许直接访问文本中的单词序列。
如果出现在数组中的偏移量增加,则可以使用二进制搜索来查找特定偏移量之间的单词。因为每次出现都有一个指向 trie 节点的反向链接,该节点包含 word 成员中的完整单词,所以很容易打印出文件中出现的任何单词,而无需扫描整个 trie。
我写了这个答案,因为我想展示如何以这种方式组合两个非常不同的数据结构,可以开辟访问数据的非常有效的方法。我不能说它是否有用,因为有用性取决于正在解决的问题。