使用嵌套哈希映射制作TRIE有什么好处?
例如,让我们有一个嵌套的哈希映射,其中每个映射只有一个字符的键。因此,对于“狗”这个词,我们会有类似myHashMap['d']['o']['g']['*'] = True
的内容。
末尾的'*'表示条目的结尾。
在书中,我从未见过这种方法,而是“经典”的Node类。为什么呢?
答案 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
。另一个替代方案可能是Array
或List
,但对我来说,当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)