最近在《程序员》杂志上看到一个.net大牛写一篇《用三叉搜索树实现高效率的“自动完成”》的文章,感觉这种方法确实不错。
对于字符串的高效处理一般都是用字典树——Trie。虽然执行也是非常快,但是用这种数据结构需要消耗非常多的内存。Trie是一种形似树的数据结构,它的每个节点都包含一个指针数组,其中每一个指针对应着字母表里的一个字母。从根节点开始,我们只要依次找到目标单词里下一个字母对应的指针,就可以一步步追查到目标。
同样,对于数据的处理一般也都是用bintree 和Trie ,而遍历表则可能不仅仅是26个字母,可能是按一个或多个字段的值为主键进行查找的,表中可能有几万或百万千万个的元素。那么就要充分考虑内存的消耗问题了。
【从bintree、Trie 到三叉搜索树】PRO*C对数据处理是先一次性数据上载到内存,然后对原始数据进行过滤、运算等处理,处理结束再后统一入库。随着上载内存的数据逐渐增多,数据操作越复杂,节点数组中保存的空指针占用的内存就越多。当内存耗用率达到一定界值后,程序的执行效率必然会急剧降低。这个可以参见《批量数据结构缓冲加载数据二叉树查找的效率优化测试》一文。
那么可不可以用一种方法来解决这种问题呢?三叉搜索树就用了一种聪明的手段解决了Trie 的内存问题。为了避免多余的指针占用内存,每个Trie节点不再用数组来表示,而表示成“树中有树”。Trie节点里每个非空指针都会在三叉搜索树里得到属于它自己的节点。
三叉搜索树包含三种类型的箭头。第一种箭头和Trie里的箭头一样的,沿着箭头前进就意味着“匹配上”了箭头起始端的元素。若没有符合才会沿着向左或向右的箭头前进。如果按照遍历表的数据,我们要找的元素应该排在当前节点上元素的前面,我们就取左箭头;反之则取右箭头。
为了取得最佳性能,数据上载应该以随机的顺序插入到三叉树中,不要排序插入。否则对应于单个Trie节点的子树会退化成链表,极大地增加了查找的成本。当然我们还可以用更复杂的方法来实现自平衡的三叉树。
另外,切记不要超出实际需求而采用华丽的数据结构。如果遍历表的数据量较小,那么直接用bintree 就可以了。