使用嵌套哈希映射实现TRIE?

时间:2013-11-11 00:53:08

标签: java algorithm data-structures hashmap trie

使用嵌套哈希映射制作TRIE有什么好处?

例如,让我们有一个嵌套的哈希映射,其中每个映射只有一个字符的键。因此,对于“狗”这个词,我们会有类似myHashMap['d']['o']['g']['*'] = True的内容。 末尾的'*'表示条目的结尾。

在书中,我从未见过这种方法,而是“经典”的Node类。为什么呢?

4 个答案:

答案 0 :(得分:2)

这是一个很好的问题,我正在思考这个问题。

Glenn的回答没有考虑Trie(或前缀树的前缀存储性质,给它另一个名字)。如果你想要的只是一本字典,那么Hashtable是一个更好的选择,但如果你想做一些自动完成风格的东西,那么Trie是理想的。对于需要对其进行排序的Trie,我也没有任何理解。

我想你提到的'经典'方法是使用字符索引数组O(1)查找来引用任何节点的子节点。对于小字母表来说,这是快速且节省空间的,但是对于非常大的字符集(Unicode),您很快就会观察到空间的限制。

您提到的另一个选择是在每个节点上都有一个HashMap,它将每个字符映射到子节点。您保留索引数组的常量查找时间(假设有一个真正的哈希实现),并且您希望每个节点不使用数千个字节存储空字符槽。

看起来像是一场全面的胜利,所以我也想知道为什么我不经常看到它。

我考虑过的一种混合方法是,如果您事先了解整个字母表,请保留char->数组索引(连续索引到您的子数组中)的哈希映射,以获得两全其美的效果。只需在前面扫描你的字典,告诉Trie你将在建筑中使用哪个unicode字符。

答案 1 :(得分:2)

我用

Map<Character, TrieMap<K, V>> children = new TreeMap<>();

我执行TrieMap。它非常好用。

使用普通Node结构的好处是,您可以将父映射的链接包装到结构中,以便您可以更轻松地迭代映射。我没有采用这种方法并在迭代时构建Stack,因为我想确保我没有使用不必要的内容来破坏结构。然后我在迭代时构建堆栈。

Trie的主要好处是当键相似时节省空间的功能 - 在我看来,为结构添加不必要的重量是愚蠢的。因此我决定只使用TreeMap。另一个替代方案可能是ArrayList,但对我来说,当TreeMap数据格式很好时,这两种方法都不像Trie那样具有空间效率。< / p>

实际上 - 代码看起来更像:

/**
 * Map each character to a sub-trie.
 *
 * Could replace this with a 256 entry array of Tries but this will handle multi-byte character sets and I can discard
 * empty maps.
 *
 * Maintained at null until needed (for better memory footprint).
 *
 */
private Map<Character, TrieMap<K, V>> children = null;

....

/**
 * Create a new set of children.
 *
 * I've always wanted to name a method something like this.
 */
private void makeChildren() {
  if (children == null) {
    // Use a TreeMap to ensure sorted iteration.
    children = new TreeMap<>();
  }
}

所以我通过确保无子节点没有浪费空Map来进一步减少内存占用(虽然我可以很容易地使用Collections.emptyMap())。

答案 2 :(得分:0)

如果每个节点只有256个条目,为什么你会考虑一个hashmap?如果你使hashmap变小,你会增加较低节点冲突的风险,并且不错的属性已经消失......如果你把它变为动态的,你将获得所有的管理开销......

答案 3 :(得分:0)

  1. 当你声明你的嵌套hashmap时 - 你会有多深 如果它不是一个固定的深度 - 那么你刚刚使用hashmap作为节点再现了一个“节点”方法
  2. hashmap-&gt; hashmap-&gt; hashmap将占用更多空间并且比使用字符串散列更慢。
  3. 哈希映射没有排序 - 所以现在你有一个未排序的地图,这真的不是特里