1.二叉搜索树(BST)
- Binary Search Tree:任意节点的左树节点都比它小,右树节点都比它大。
好处:
- 不管是插入还是读取的时候,只需要跟每一层中一个节点比较就好了
- 查询的复杂度完全依赖于树的深度
- 读和写的复杂度都是O(logn)
说到O(logn),很多人可能想到二分查找法
与二分查找对比:
- 二分查找:在一个有序的数组中,先找中间的值,如果当前值比中间值大,那么就选它的右边这一部分查找,如果当前值比左边值小,就选左边这一部分查找。
- 这个思想跟BST是比较像的,但是有序数组在最差情况下是logn,BST最差情况下会退化成n。
- 那么,为什么在很多情况下我们使用的是BST而不是这种有序数组的二分查找?
- 因为二分查找插入的时候是比较费劲的,我们插入一个数,找到他的位置以后,该位置后面是数全部要往后移一位。插入的复杂度就是O(n);
坏处:
- 当数据是连续自增或者自减的时候,整个树变成了一条线,树的深度也变成了O(n)
- 查询和插入的复杂度都变成了O(n)
2.自平衡二叉树(AVL树)
- Adelson-Velsky and Landis Tree:首先本身是一个BST,并且满足任意一个节点左子树和右子树深度差小于等于1。
- 自平衡二叉树的读和写的复杂度都是O(logn),并且在最差情况下也是O(logn)
- 他对BST的一种比较好的优化。
3.红黑树
- Red–black tree:
- 每个节点要么红,要么黑
- 根节点必须是黑节点
- 叶子节点必须是黑节点(叶节点包括null节点)
- 红节点的子节点必须是黑节点
- 新插入 的节点必须是红节点
- 从任意节点出发,到叶子节点的所有简单路径上,所拥有的黑节点数必须是一样的
- 可以看出大多是黑节点,红节点的生存条件很恶劣
- 叶子节点必须是黑节点就保证黑节点数达到一半以上
- null节点也是叶子节点的情况下,就保证我们的二叉树是一颗满二叉树
定义这么多条件的目的是什么?
- 有人说,红黑树也是一种自“平衡”的二叉树,只不过红黑树的“平衡”标准跟AVL不一样。
- AVL树的平衡是想保证左子树和右子树的深度相差为1。这样是一种比较严苛的平衡条件。
- 红黑树的平衡条件则是两条路径上的黑节点数相同。
两条路径上的黑节点数相同,他的上下界是什么?
-
上界:如果两条路径都是黑节点,那两条路径上的黑节点数相同就代表两条路径上的节点数相同。深度差为0;
-
下界:最坏的情况就是一条路径上没有红节点,另一条路径上有大量红节点,红节点的子节点必须是黑节点,红节点最多的情况是红黑相间,节点数差一倍。
-
AVL树的下界是左右深度差1,
-
红黑树的左右深度差为1倍以内,这就是红黑树要实现的效果。
红黑树为什么要实现这样一种平衡?
- AVL树的平衡条件是左右深度差1,这个条件是相当苛刻的,就会导致很多情况下,都不满足平衡的条件,不满足条件的话就需要进行一些变化,变化的话就会一些时间。但是红黑树心目中的平衡条件更加宽松一些,这样就使得我们插入数据的时候,树的变化会比较少,所以红黑树写的性能会更高一些。
这里就涉及到一个面试题:java8中,HashMap的实现是数组加一个链表,当链表的长度达到8,并且数组长度大于64以后,就会把链表转化成红黑树,java8中为什么要选择红黑树这种存储结构,而不是BST或AVL树? Java基础--集合总结(含HashMap jdk1.7/1.8源码对比分析)
深度差差一倍,还能保证O(logn)吗?
- 搜索和查询的书时间复杂度完全是依赖树的深度,完全遮住红节点就是O(logn),最差情况就是红黑相间O(2logn),还是O(logn)。