二叉查找树
二叉查找树又叫二叉搜索树。特点是,在树中任意一个节点,其左子树的每个节点的值,都要小于这个节点的值,而右节点的值都大于这个节点的值。
- 查找
从根节点递归,注意判断 null,对当前节点进行比较,等于则返回,小于则往左递归,否则往右递归。
- 插入
思路类似查找操作,遇到 null 则应该直接插入。如果有相同的节点,考虑使用链表法解决,或者选定一个方向插入,比如插入左子树最右边,或则插入右子树最左边。这时树左右子树同当前节点的关系就变成了不大于、不小于的关系了,而原来的关系是小于、大于关系。这种情况下,查找操作和删除操作也应该做相应改变。
- 删除
仍然类似查找操作,遇到 null 返回 false。最简单的删除操作应该就是这样:
- 没有孩子,直接结束。
- 只有一个孩子,孩子替换到当前位置
- 两个孩子,将删除节点的左孩子移到被删除节点的位置,将左孩子的右子树插入右孩子的最左边,然后把右子树替换在左子树的右儿子位置。
- 输出有序序列
按中序遍历输出即可。
- 复杂度分析
最好情况下,树的高度小于等于 log2n,其中 n 是节点个数。此时查找、插入、删除操作的时间复杂度是 O(log2n)。最坏情况下,时间复杂度是 O(n),此时树退化成链表。
有了高效的散列表,为什么还需要二叉查找树呢
我觉得除了历史原因——跳表比平衡树出现得晚以外,平衡树已经有了成熟的方案保证极端情况下的性能不会退化到明显的程度。而散列表、跳表等结构,还是可能随着插入删除等操作的进行,极端情况下有数量级的性能退化的。
总的来说,有以下一些原因:
散列表中的数据是无序存储的,若要输出有序数据,需要进行排序。而二叉搜索树可以用中序遍历在 O(n) 时间内完成。
散列表扩容耗时很多,遇到散列冲突,性能不稳定。虽然二叉搜索树性能不稳定,但是它的优化版,平衡树的性能一直很稳定。
尽管散列表查找的性能是 O(1) 级的,但是因为散列冲突的存在,和哈希函数的耗时,性能不一定比 O(logn) 级快,毕竟 logn 是一个很小的数字。
散列表的构造要考虑的点很多,比如散列函数的设计、冲突解决的方法、初始容量和负载因子、扩容、缩容等。而平衡树就不需要考虑那么多了,唯一的问题平衡性已经有成熟的方案了。
但,要说散列表比树的优点,也是很多。。这两种数据结构不应该是有绝对的优劣的,应该根据具体场景具体选择。
求给定一颗二叉树的确切高度
- 递归法
这种方法最简单好理解。 树高 = 根节点高度 = max(leftHeight, rightHeight) + 1
- 队列法
类似层次遍历,维持一个队列,同时设变量记录下当前层数,当前层剩余未遍历个数,下一层个数。遍历完这个队列即可得到树的高度,也避免了过大的内存开销和过深的递归可能出现的问题。