AVL树,红黑树,B树,B+树,Trie树都分别应用在哪些现实场景中?

关注者
8,709
被浏览
811,098

60 个回答

AVL树: 最早的平衡二叉树之一。应用相对其他数据结构比较少。windows对进程地址空间的管理用到了AVL树。

红黑树: 平衡二叉树,广泛用在C++的STL中。如map和set都是用红黑树实现的。

B/B+树: 用在磁盘文件组织 数据索引和数据库索引。

Trie树(字典树): 用在统计和排序大量字符串,如自动机。

AVL树是平衡二叉搜索树的鼻祖,它的平衡度也最好,左右高度差可以保证在「-1,0,1」,基于它的平衡性,它的查询时间复杂度可以保证是O(log n)。但每个节点要额外保存一个平衡值,或者说是高度差。这种树是二叉树的经典应用,现在最主要是出现在教科书中。AVL的平衡算法比较麻烦,需要左右两种rotate交替使用,需要分四种情况,是数据结构课的最理想课后作业之一。这里要重点强调,AVL插入新节点所需要的最大旋转次数是常数,再说一遍,是常数,不要为这个再来回复或者私信我了。这个也有证明,不需要一直旋转到根节点。

红黑树一样也是平衡二叉搜索树,也是工业界最主要使用的二叉搜索平衡树。但平衡度红黑树没AVL那么好。也就是说,如果从高度差来说,红黑树是大于AVL的,其实也就代表着它的实际查询时间(最坏情况)略逊于AVL的。数学证明红黑树的最大深度是 2\cdot log_{2} (n+1) , 其实最差情况它从根到叶子的最长路可以是最短路的两倍,但也不是很差,所以它的查询时间复杂度也是O(log n)。从实现角度来说,保存红黑状态,每个节点只需要一位二进制,也就是一个bit(有些做法,可以把这个bit塞到其他地方,就可以不占用额外空间了)。可AVL每个节点需要额外存储一个平衡值(数)(按照评论中大神韦易笑的说法,avl的平衡值可以用两个bit来存储,然后塞到指针里,惊才绝艳的搞法。当然指针少两位会有什么问题,我就不多想了,你去找他,他有个很好的avl实现)。所以(这里不能用所以,因为谁知道真实原因是啥),一般工业界把红黑树作为一种更通用的平衡搜索数来用,Java用它来实现TreeMap, C++的std::set/map/multimap等等。

以上都是二叉(binary)树,其实没有太多实质差别。他们的实现都比较复杂,如果做得不好会带来性能的隐患,所以强烈建议入行不足10年的朋友尽量使用系统中的已有默认实现,不管是avl还是红黑树,这两个有什么就用什么,千万别自己写代码,能写也别写,留个坑给别人不合适。

二叉平衡搜索树的问题在于每次插入和删除都有很大可能需要进行重新平衡,数据就要不停的搬来搬去,在内存中这问题不是特别大,可如果在磁盘中,这个开销可能就大了。

有兴趣的朋友还可以看看2-3-4 tree,它等价于红黑树,可以互相转换。它每个节点可以最多有四个孩子,重新平衡操作的次数稍稍少了一点点。以上这些树绝大多数应用都是作为内存中的数据结构。

B/B+ 树就是N叉(N-ary)平衡树了,每个节点可以有更多的孩子,新的值可以插在已有的节点里,而不需要改变树的高度,从而大量减少重新平衡和数据迁移的次数,这非常适合做数据库索引这种需要持久化在磁盘,同时需要大量查询和插入操作的应用。

以上几种树都是有序的,如果你采用合适的算法遍历整个数,可以得到一个有序的列表。这也是为什么如果有数据库索引的情况下,你order by你索引的值,就会速度特别快,因为它并没有给你真的排序,只是遍历树而已。

Trie并不是平衡树,也不一定非要有序。它主要用于前缀匹配,比如字符串,比如说ip地址,如果字符串长度是固定或者说有限的,那么Trie的深度是可控制的,你可以得到很好的搜索效果,而且插入新数据后不用平衡。不过Trie不像B-tree通用性那么强,你需要针对你自己的实际应用来设计你自己的Trie,比如说你做个字典应用,是用26个字母,还是用unicode来前缀匹配?如果是ip地址搜索,是用二进制来前缀拼配,还是八进制来匹配?