二叉树基础(下):有了如此高效的散列表,为什么还需要二叉树?
特殊的二叉树:二叉查找树,支持动态数据集合的快速插入、删除、查找操作
二叉查找树(Binary Search Tree)
二叉查找树要求,在树中的任意一个节点,其左子树的每个节点的值都要小于这个节点的值,而右子树的值都要大于该节点的值
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;
}
}
}
2.二叉查找树的插入操作
从根节点开始,依次比较要插入的数据和节点的大小关系,若要插入的数据比节点数据大,并且右子树为空,直接插到右子节点位置,不为空,再递归遍历右子树查找插入位置
public void insert(int data){
if(tree == null){
tree = new Node(data);
return;
}
Node p = tree;
while(p != null ){
if(data > p.data){
p.right = new Node(data);
return;
}
p = p.right;
}else(
if(p.left == null){
p.left = new Node(data);
return;
}
p = p.left;
)
}
3.二叉查找树的删除操作
1.要删除的节点没有子节点,只需要将父节点中指向要删除节点的指针置为Null
2.要删除的节点只有一个子节点(左子节点或者右子节点),只需要更新父节点中指向要删除结点的指针,让其指向要删除节点的子节点即可
3.要删除的节点有两个子节点,需要找到该节点的右子树中的最小节点,替换到要删除的节点上,再删除掉该最小节点
public void delete(int data){
Node p = tree; //p指向要删除的节点,初始化指向根节点
Node pp = null; //pp记录的是p的父节点
whie( 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;
}
4.二叉查找树的其他操作
中序遍历二叉查找树可以输出有序的数据序列,也叫二叉排序树
支持重复数据的二叉查找树
二叉查找树存储的是一个包含很多字段的对象,利用对象的某个字段作为键值来构建二叉查找树,把对象中的其他字段叫做卫星数据
如果存储的两个对象键值相同,怎样处理二叉查找树的操作?
1.每个节点不仅会存储一个数据,可以通过链表和支持动态扩容的数组等数据结构,把值相同的数据都存储同一节点
2.每个节点荣然只存储一个数据,在查找插入过程中,如果碰到一个节点的值与要插入数据的值相同,将该要插入的数据放到该节点的右子树,把这个新插入的数据当做大于这个节点的值处理
当要查找数据的时候,遇到值相同的节点,继续在右子树中查找,直到遇到叶子节点才停止
删除操作中,先查到到每个要删除的节点,再按删除操作的方法依次删除
时间复杂度与树的高度成正比,O(Height)
为什么还用二叉查找树?
1.散列表中数据是无序的,如果要输出有序的数据,先排序,而对于二叉查找树,只需要中序遍历即可,在O(N)时间复杂度内输出有序的数据序列
2.散列表耗时很多,当遇到散列冲突的时候性能不稳定,常用的平衡二叉树性能很稳定
3.散列表的构造比二叉树要复杂,比如散列函数的设计、冲突如何解决、扩容和缩容
如果通过编程求出一颗给定二叉树的确切高度?
1.深度优先思想的递归,分别求左右子树的高度,当前节点的高度就是左右子树中较大的那个+1
2.采用层次遍历的方式,每一层记录都记录下当前队列的长度