使用平衡二叉搜索树的原因
之前讲过,如果要从n个数种搜索一个数,使用动态数组的时间复杂度是O(n),而在动态数组中插入、删除元素的时间复杂度也是O(n),即使维护一个有序的动态数组,使用二分查找,搜索的时间复杂度是O(logn),插入和删除的时间复杂度仍然是O(n)。而使用二叉搜索树,则可以将插入、删除、搜索的复杂度都降到O(logn)。但是,不是所有的情况都可以做到O(logn)的复杂度,比如下面这棵二叉搜索树,搜索的时间复杂度就是O(n)了。
而对于下面这棵树,则仍保持O(logn)的效率。
而当n相当大时,两者的效率是天差地别的,比如当n=1000000时,二叉搜索树的高度h=logn=20。
什么时候bst退化为链表?
- 添加节点的时候:如不断插入更大的节点,则bst慢慢就会像一条倾斜的链表转化。
- 删除节点的时候:如在删除节点后使得bst每次层的节点数都趋于1,bst也会向链表转化。
那么有没有办法让bst不退化为链表,保持插入、删除、搜索O(logn)的时间复杂度?
平衡
平衡:当节点数量固定时,左右子树的高度越接近,这棵二叉树就越平衡(高度越低),最理想的状态是像满二叉树,完全二叉树那样,在相同节点数的二叉树中,高度是最小的。
平衡二叉搜索树
由于插入删除的元素的顺序是无法预测的,所以对bst进行的调整只能是在完成插入删除操作后。所以我们应该在:在节点的添加、删除操作之后,想办法让二叉搜索树恢复平衡。但如果进行较多次的调整,可以将bst调整为完全二叉树的形态,但是如果次数太多,反而丢失了降低复杂度的初衷,因此,我们应用尽量少的调整次数达到适度平衡即可。一棵达到适度平衡的二叉搜索树,可以称之为:平衡二叉搜索树。
常见的平衡二叉搜索树有:
- AVL树:Windows NT 内核中广泛使用。
- 红黑树:在c++ STL的map、set,Java中的TreeMap、TreeSet、HashMap、HashSet以及Lunix的进度管理中广泛使用。