关于Trie字典树性能问题

来源:10-3 Trie字典树的查询

Klaus_Mikaelson

2018-06-16

老师,您好:

关于Trie字典树,您在视频中讲到,Trie查询的速度和元素的个数没有关系,和单词的长度有关系。

下面的性能对比包括读取文件的时间,只包括插入和查询:

System.out.println("\tTotal words: " + words1.size());
long start = System.nanoTime();
for (String word : words1) {
    set.add(word);
}
System.out.println("\tTotal different words: " + set.size());
for (String word : words1) {
    set.contains(word);
}
long end = System.nanoTime();
return (end - start) / 1000000000.0;

您也进行了LinkedSet、BSTSet和TrieSet进行了性能对比。我使用双城记作为数据源,性能对比如下所示:

TrieSet       Time: 0.165550323
BSTSet        Time: 0.146506406

后来我把数据源改成了词汇量更大的战争与和平,发现Trie比二分搜索略更慢:

TrieSet       Time: 0.195874424
BSTSet        Time: 0.145736233

后来我估计是因为插入的操作太耗时了,所以就单独统计查询操作的时间:

傲慢与偏见:

TrieSet       Time: 0.045231067
BSTSet        Time: 0.04074773

词汇量更大的双城记:

TrieSet       Time: 0.059717239
BSTSet        Time: 0.059854082

词汇量更大的战争与和平

TrieSet       Time: 0.092482286
BSTSet        Time: 0.210517965


为啥Trie的插入的时间比BST还要长呢?

代码地址



写回答

1回答

liuyubobobo

2018-06-16

赞实验精神:)


我们这里实现的Trie,效率比较低的核心原因,是Trie中使用了TreeMap。TreeMap的底层实现是红黑树,虽然从复杂度分析的角度,红黑树是很高效的(各项操作均为O(logn)),但是,一定要注意,复杂度分析关注的是n趋于无穷的情况。对于小样本数据,一个复杂的复杂度更优的算法,很有可能比不上一个简单的复杂度更低的算法。因为常数项的不同。如果你曾经看过我的《算法与数据结构》,就一定会有印象,在我们编写的归并排序和快速排序算法中,对于n比较小的情况,我们转而使用复杂度更高的插入排序去优化,就是这个原因。虽然插入排序的复杂度比归并排序,快速排序都要高,但是只有在n很大的情况才能显现出来,在n很小的情况下,插入排序更快:)


我们的Trie中使用的TrieMap(红黑树)同理。红黑树中由于有复杂的旋转操作(在这个课程的第十三章会介绍),虽然整体平均复杂度更低,但是对于小样本数据,是没有太大的性能优势的。所以,在这里,其实,使用HashMap代替更好。如果你能保证你存储的单词只包含小写字母,直接使用数组将是更快的:)为了验证这一点,我编写了一套测试程序,参考这里:https://github.com/liuyubobobo/Play-with-Data-Structures/tree/master/10-Trie/Optional-01-Trie-Using-HashMap-and-Array/src


其中,

Trie仍然是我们在这个课程中,内部结点使用TreeMap做映射的Trie;

Trie2的内部结点中,使用HashMap做映射;

Trie3的内部结点中,使用一个包含26个元素的数组做映射,这就限制了Trie3中存储的单词,只能包含小写字母。


其中,在Main的测试用,我同时加载了《傲慢与偏见》和《双城记》两本书的单词,以稍微增大数据量。


在我的计算机上,一次典型的运行结果是这样的:)

Total different words: 12177
BSTSet: 0.464858395 s
Total different words: 12177
TreeMap Trie: 0.394749343 s
Total different words: 12177
HashMap Trie: 0.293205009 s
Total different words: 12177
Array(Map) Trie: 0.146712532 s


其中,基于TreeMap的Trie将是最慢的:)

基于数组的Trie最快,因为相比HashMap,数组也不需要计算哈希函数:)(关于HashMap的内部原理,可以参见这个课程的第十四章)


有兴趣可以下载一下我的测试代码,看看在你的环境下是否有和我的环境下类似的结果:)


P.S.1:

注意:在这里,为了能让我们的第三个Trie可以运行,FileOperation的分词部分有所修改,只判断isLetter是不够的,因为双城记中包括法文字符,所以我自己写了一个isEnglishLetter的方法,相信聪明的你一看就懂:)


P.S.2:

对于你给出的最后两个词汇量更大的测试,Trie还是有优势的:)


P.S.3

最后,对于Trie来说,其实还有一个重要的时间开销。由于Trie消耗的空间比较大(每个节点会有若干个指针),所以给这些承载指针的空间(TreeMap,HashMap或者数组)开辟内存也是一个额外的时间消耗:)

7
2
hinsss
波波老师真的太赞了 > 3 <
2021-08-23
共2条回复

玩转数据结构

动态数组/栈/队列/链表/BST/堆/线段树/Trie/并查集/AVL/红黑树…

6221 学习 · 1705 问题

查看课程