1.只读场景下的高效查找
在纯内存、数据随机分布 、且只关注查找性能 的前提下,AVL 树(二叉平衡树)通常是搜索效率最高的选择。
-
AVL 树通过严格的平衡策略(任意节点左右子树高度差 ≤ 1),将树高控制在理论最小值 ⌊log₂n⌋。
-
这意味着每次查找所需的比较次数最少,常数因子也较小。 ✅ 优势 :查找速度最快
❌ 代价:为了维持这种"绝对平衡",插入或删除时可能需要多次旋转(甚至从叶子一路旋转到根),维护成本高;而且如果要范围查询的话需要中序遍历avl树
然而,现实中极少存在"只读不写"的系统。一旦引入动态更新,AVL 树的劣势就显现出来了。
2.现实:需要频繁插入和删除(动态有序集合)
avl树要求的是绝对的平衡,左右子树高度差不能超过1,但是这种要求有的时候对实际的搜索太严格了,而且实际应用中很少有只搜索的,大部分都是搜索+动态增删。
->红黑树
而红黑树只保持近似平衡,它只是保证从根到任意叶子的最长路径不超过最短路径的两倍。虽然查找性能略慢于avl树,但是插入删除节点都更快,综合性能更好。所以在java的框架中很多地方都使用红黑树(hashmap等)
3.有范围查询的需求
avl和红黑树虽然能通过中序遍历实现范围查询,但是,遍历过程依赖递归或者栈,无法直接跳到下一个元素,缺乏显式的顺序连接,效率不高。
范围查询的最佳结构就是有序遍历+快速定位起点 ,通过在有序链表上建立索引的方式提升搜索的性能,一种类似于有序链表+搜索树的结构,这种结构的主要应用场景是在数据库中(通过键来搜索数据)
4.基于磁盘实现的数据库:
磁盘场景下的搜索情况与内存有明显差异,因为内存读取不同节点都是在内存中,速度很快,但是如果在磁盘中的话,加载不同的节点就非常慢了,所以这种情况下就需要在一个数据页中存放尽量多的数据,从而避免多次加载数据页
->b树和b+树
b树中每个节点即存放键也存放数据,这样会更快的找到数据,但是每个节点存放的数据就更少了(相较于只存放键来说),范围查询的话也不太方便
b+树则是每个非叶子节点只存放键,只在叶子节点中存放数据,并且由于叶子节点都是数据,所以可以形成一个链表,非常有利于范围查询;同时由于非叶子节点中只存放键,所以数据页中就可以放更多的键,从而只访问一个节点,就可以排除大量的键,树的高度也更低了,磁盘的访问次数也大大减小。**这也就是上文所提到的有序链表+搜索树的结构。**通过叶子节点实现有序链表,同时将上层的搜索树使用多叉树来减小节点数实现更少的磁盘访问次数。
可以说b+树是磁盘实现数据库下的最佳实践。
5.内存实现的数据库:
内存实现的数据库实际上是在内存和磁盘数据库中间起到一个缓冲的作用,所以一般来说这样的数据库对性能要求都比较高
但是对于有序链表+搜索树这种数据结构来说,一个节点不仅要维护自己作为搜索树的左右子树节点,还要维护自己作为链表的前后节点,所以上层的搜索树往往比较复杂
->跳表
快表通过上层仍然是有序链表的方式改进搜索树的这种复杂情况,相当于把搜索树的每个节点只保留右子树,通过多层这样的结构来保证搜索的效率(上层搜索粒度大,下层搜索粒度小)
这种数据结构不仅实现了搜索+范围查找的目的,而且避免了为了维护搜索树平衡所带来的消耗。