二叉查找树
1.1二叉树与散列表对比
二叉树最大的特点就是支持动态的快速插入、删除、查找等操作。
散列表也是支持这些操作的,而且散列表的这些操作都是比二叉树要高效,时间复杂度都是o(1),既然有了这么高效的散列表,为什么还要有二叉树呢?
- 一,散列表是无序存储的,如果要有序输出,还是需要进行排序的,对于顺序二叉树来说,直接中序遍历输出的结果就是有序的。
- 二,散列表的扩容缩容耗时很多,虽然我们可以吧这些操作均摊到其他的步骤上,但是整体来说的好事还是一定的。二叉树之需要考虑平衡的方案,而且这个方案比较成熟、固定。
- 三,散列表存在散列冲突,会导致散列表的性能不太稳定,平衡二叉树的性能非常稳定,时间复杂度也会稳定在O(logn)。
总体来说散列表的设计比较复杂,散列函数的要求比较高,并且散列表的装载因子不能太大,特别是对于开放寻执法解决冲突的散列表,会浪费一定的存储空间。
两者各有各的优势,散列表效率极高,散列函数的设计和装载因子的设定根据对应的场景来确定。二叉树比较稳定,方案成熟,效率来讲偏低。
1.2二叉查找树(Binary Search Tree)
二叉查找树又叫二叉搜索树,是一种专门没了快速查找而生的数据结构。
二叉查找树要求,在树中的任意一个节点,其左子树中的每个节点的值,都小于这个节点。而右子树的每个节点的值,都大与这个节点。
1.2.1 二叉查找树的查找操作
二叉查找树要查找一个数,首先取根节点比较,大于根节点就在右子树中递归查找,小于根节点就在左子树中递归查找。
代码如下:
public class BinarySearchTree{
private Node tree;
public Node find(int data) {
Node p = tree;
while (p != null) {
if (data < p.data) p = p.left;
else if (data > p.data) p = p.right;
else return p;
}
return null;
}
public static class Node {
private int data;
private Node left;
private Node right;
public Node(int data){
this.data=data;
}
}
}
1.2.2 二叉查找树的插入操作
二叉查找树的插入操作类似于查找操作,插入的数据都是在叶子节点上的。
public void insert(int data) {
if (tree == null) {
tree = new Node(data);
return;
}
Node p = tree;
while (p != null) {
if (data > p.data) {
if (p.right == null) {
p.right = new Node(data);
return;
}
p = p.right;
} else { // data < p.data
if (p.left == null) {
p.left = new Node(data);
return;
}
p = p.left;
}
}
}
1.2.3 二叉查找树的删除操作
二叉查找树的删除操作就比较复杂了。大致可以分为三种情况。
- 删除的节点有左右两个字节点。需要找到对应节点右子树中的最小值,替换掉这个节点。然后删除右子树的最小节点。
- 删除的节点没有子节点。直接删除就可以了。
- 删除的节点只有一个字节点,把删除节点的父节点指向对应的字节就可以了。注意,左子节点还是左子节点,右子节点也还是右子节点。
public void delete(int data) {
Node p = tree; // p指向要删除的节点,初始化指向根节点
Node pp = null; // pp记录的是p的父节点
while (p != null && p.data != data) {
pp = p;
if (data > p.data) p = p.right;
else p = p.left;
}
if (p == null) return; // 没有找到
// 要删除的节点有两个子节点
if (p.left != null && p.right != null) { // 查找右子树中最小节点
Node minP = p.right;
Node minPP = p; // minPP表示minP的父节点
while (minP.left != null) {
minPP = minP;
minP = minP.left;
}
p.data = minP.data; // 将minP的数据替换到p中
p = minP; // 下面就变成了删除minP了
pp = minPP;
}
// 删除节点是叶子节点或者仅有一个子节点
Node child; // p的子节点
if (p.left != null) child = p.left;
else if (p.right != null) child = p.right;
else child = null;
if (pp == null) tree = child; // 删除的是根节点
else if (pp.left == p) pp.left = child;
else pp.right = child;
}
1.3 二叉查找树的时间复杂度分析
如果二叉树的左右分布及其不平衡,时间复杂度就会退化到O(n),不管是二叉树的那种操作
他的时间复杂度都是根树的高度成正比。可以看出在二叉树左右相对平衡的情况下的时间复杂度为
O(long)。