二叉查找树的定义(Binary Search Tree)
左子树每个节点的值要小于节点值,右子树每个节点的值都要大于该节点的值。对于大小相等的节点,两种做法,链表或者数组的方式保存,一种是当做大于该节点放在右子树,本文取第二种方法
二叉查找树的查找操作
遍历或者递归查找树,如果该值小于节点的值从左子树查找,如果大于大于根节点从有子树查找
1 | //遍历方法 |
二叉查找树的添加操作
新插入的值一般在叶子节点,所以遍历树,如果小于节点值,并且做节点是空就插入到左子树,如果大于节点的值并且节点为空插入有子树,否则继续遍历
1 | //遍历 |
二叉查找树的删除
- 找到该节点, 如果遇到冲突,放在数组中
- 如果查找的节点叶子节点直接将父节点指向null
- 如果只有左(右)子树,将该节点指向改节点中的左(右)子树
- 如果既有左子树,又有右子树,找第一个大于该节点的节点a,将a的值复制到该节点,然后删除a。
- 从数组逆向遍历一个个删除。
1 | public static TreeNode delNode(TreeNode tree, int v) { |
时间复杂度
查找、删除、添加的逻辑都是类似的时间复杂度和树的高度相关。节点和高度的关系如下:
以完全二叉树为例,假设树的层级是H,非叶子节点是:1+2+4+8+…+(2的H-1次方),叶子节点是【1~2的H次方】。H就是log(n-1)+1~Logn之间,所以时间复杂度是logN
最坏情况
破坏平衡退化成链表。
搜索树和散列比的其他功能
搜索数能很方便的找到最小节点、最大节点、找到前驱节点和后置节点代码如下。
搜索数如果想按从小到大打印可以通过中序遍历2Logn比散列要快。
和散列对比其他的优势:
- 散列虽然查找是o(1),但是考虑到hash函数以及hash冲突要遍历链表以及开发寻址,所以不一定效率高
- hash冲突导致性能不稳定,搜索树通过平衡性能很稳定
- 散列需要扩容和缩容额外需要资源
- 负载因子不能太大容易造成内存浪费