为什么要创建平衡二叉搜索树
当一个二叉搜索树每个结点的C(i)为该结点的层次数。最坏情况下,当先后插入的关键字有序时,构成的二叉排序树蜕变为单支树,树的深度为其平均查找长度(n+1)/2(和顺序查找相同),最好的情况是二叉排序树的形态和折半查找的判定树相同,其平均查找长度和log 2 (n)成正比。
所以要创建一个尽量平衡的树降低查找时间复杂度
平衡二叉搜索树的数据结构
平衡二叉树定义(AVL):它或者是一颗空树,或者具有以下性质的二叉排序树:它的左子树和右子树的深度之差(平衡因子)的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树。
所以平衡二叉搜索树是在二叉搜索树的基础上又增加了一个树高以判断是否平衡,其他均与二叉树相同。
public class tree {
private Object data;
private tree left;
private tree right;
private Integer height = 1;
public Integer getHeight() {
return height;
}
public void setHeight(Integer height) {
this.height = height;
}
public tree (){}
public tree(Object data) {
this.data = data;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public tree getLeft() {
return left;
}
public void setLeft(tree left) {
this.left = left;
}
public tree getRight() {
return right;
}
public void setRight(tree right) {
this.right = right;
}
}
平衡二叉搜索树如何添加节点
平衡二叉搜索树添加整个实现过程是通过在一棵平衡二叉树中依次插入元素(按照二叉排序树的方式),若出现不平衡,则要根据新插入的结点与最低不平衡结点的位置关系进行相应的调整。分为LL型、RR型、LR型和RL型4种类型,各调整方法如下
首先是LL型调整
此时需要1、将9的左孩子B提升为新的根结点;2、将原来的根结点9降为8的右孩子;3、各子树按大小关系连接(8L和9R不变,8R调整为9的左子树)。4、调整连接后的树的高度。
public tree ll_rotate(tree y){
tree x = y.getLeft();
y.setLeft(x.getRight());
x.setRight(y);
y.setHeight(Math.max(
height(y.getLeft()),
height(y.getRight())) + 1);
x.setHeight(Math.max(
height(x.getLeft()),
height(x.getRight())) + 1);
return x;
}
RR型
与LL型思想一致。
public tree rr_rotate(tree y){
tree x = y.getRight();
y.setRight(x.getLeft());
x.setLeft(y);
y.setHeight(Math.max(
height(y.getLeft()),
height(y.getRight())) + 1);
x.setHeight(Math.max(
height(x.getLeft()),
height(x.getRight())) + 1);
return x;
}
LR型
由于在9的左孩子(L)的右子树®上插入新结点,使原来平衡二叉树变得不平衡,此时9的平衡因子由1变为2。下图是LR型的最简单形式。显然,按照大小关系,结点8应作为新的根结点,其余两个节点分别作为左右孩子节点才能平衡。
思路:1、将7的右孩子8提升为新的根结点;2、将原来的根结点9降为8的右孩子;3、各子树按大小关系连接(7L和9R不变,8L和8R分别调整为7的右子树和9的左子树)。
public tree lr_rotate(tree y){
tree x = y.getLeft();
y.setLeft(rr_rotate(x));
tree t = ll_rotate(y);
return t;
}
RL型
思想与LR相同
public tree rl_rotate(tree y){
tree x = y.getLeft();
y.setRight(ll_rotate(x));
tree t = rr_rotate(y);
return t;
}
知道了LL型、RR型、LR型和RL型4种类型,各调整方法,接下来就是如何判断每种情况
(1)首先要知道是否平衡,写一个getBalance函数,如果返回值得绝对值大于1,那么这个节点一定不平衡,说明这个节点需要调整。
这里需要注意一点,调整得必须是最低不平衡节点。所以添加节点的算法的使用递归是十分方便的,因为递归方法弹出栈时直接判断是否平衡,这时一定是最低不平衡节点。
public int getBalance(tree node){
if(node == null) {
return 0;
}
return height(node.getLeft()) - height(node.getRight());
}
(2)如果balance大于1那么就是LL型或者LR的情况,新节点小于不平衡节点的左子树的值那么就是LL型,如果大于就是LR型
(3)如果balance小于-1那么就是RR型或者RL的情况,新节点大于不平衡节点的左子树的值那么就是RR型,如果小于就是RL型
完整添加代码
public void add(Object data){
tree node = new tree(data);
if(root == null){
root = node;
}
else{
root = f(this.root,node);
}
size++;
}
private tree f(tree curNode,tree newNode) {
Comparable c = (Comparable) curNode.getData();
Comparable n = (Comparable) newNode.getData();
if(c.compareTo(n) == -1){
if(curNode.getRight() != null){
curNode.setRight(f(curNode.getRight(),newNode));
}else{
curNode.setRight(newNode);
}
}else if(c.compareTo(n) == 1){
if(curNode.getLeft() != null){
curNode.setLeft(f(curNode.getLeft(),newNode));
}else{
curNode.setLeft(newNode);
}
}else {
System.out.println("输入错误");
return null;
}
//保证添加了节点,调整节点
curNode.setHeight(
Math.max(
height(curNode.getLeft()),
height(curNode.getRight())
) + 1);
int balance = getBalance(curNode);
//判断你是什么型 ll rr lr rl
//ll型
if(balance > 1 && n.compareTo(curNode.getLeft().getData())==-1)
return ll_rotate(curNode);
//rr
if(balance < -1 && n.compareTo(curNode.getRight().getData())==1)
return rr_rotate(curNode);
//lr
if(balance > 1 && n.compareTo(curNode.getLeft().getData())==-1){
curNode.setLeft(rr_rotate(curNode.getLeft()));
return ll_rotate(curNode);
}
//rl
if(balance < -1 && n.compareTo(curNode.getRight().getData())==-1){
curNode.setLeft(rr_rotate(curNode.getLeft()));
return ll_rotate(curNode);
}
return curNode;
}
平衡二叉搜索树如何删除节点
平衡二叉搜索树如何添加节点,只需要像二叉搜索树删除节点一样,然后删除之后判断是否平衡,不平衡再调整平衡就行了
删除节点有三种情况
(1)需要删除的节点左子树和右子树为空
此时可以直接将这个节点指空,最后返回空,使这个节点的父节点的子树设为空。
(2)如图所示,需要删除的节点左子树或者右子树为空,此时将现在的节点指向它的左子树(如果左子树为空则指向右子树)。
(3)如图所示,需要删除的节点的左右子树均存在,此时需要将左右子树中节点值跟它最接近的节点,变成此节点,这里取最接近它比他大的节点,也就是此节点右子树最深的左子树,也就是图中的10,然后调用删除节点的算法递归将子树15中的10删除
然后判断是否不平衡
(1)balance>1,为LL型或者LR型,此时判断它的左子树的高度差,如果大于等于零则为LL型,如果小于零则为LR型。
下图为LR型例子,16的balance>1,10balance<0
(2)balance<-1,为RR型或者RL型,此时在判断它的右子树的高度差,如果小于等于零则为RR型,如果大于零则为RL型。
public void remove(Object data){
this.root = deleteNode(this.root,data);
if(size>0)
size--;
}
public tree minValueNode(tree Node){
tree cur = Node;
while(cur.getLeft()!=null){
cur = cur.getLeft();
}
return cur;
}
public tree deleteNode(tree root , Object data){
if(root == null) return null;
Comparable comparable = (Comparable) root.getData();
if(comparable.compareTo(data)==1)
root.setLeft(deleteNode(root.getLeft(),data));
else if(comparable.compareTo(data)==-1)
root.setRight(deleteNode(root.getRight(),data));
else{
if(root.getLeft()==null||root.getRight()==null){
tree temp = root.getLeft()!=null ? root.getLeft():root.getRight();
if(temp == null){
temp = root;
root = null;
}else{
root = temp;
temp =null;
}
}else{
tree temp = minValueNode(root.getRight());
root.setData(temp.getData());
root.setRight(deleteNode(root.getRight(),temp.getData()));
}
}
if(root == null)
return root;
root.setHeight(
Math.max(
height(root.getLeft()),
height(root.getRight())
) + 1);
int balance = getBalance(root);
//ll型
if(balance > 1 && getBalance(root.getLeft())>=0)
return ll_rotate(root);
//rr
if(balance < -1 && getBalance(root.getRight())<=0)
return rr_rotate(root);
//lr
if(balance > 1 && getBalance(root.getLeft())<0){
root.setLeft(rr_rotate(root.getLeft()));
return ll_rotate(root);
}
//rl
if(balance < -1 && getBalance(root.getRight())>0){
root.setRight(rr_rotate(root.getRight()));
return rr_rotate(root);
}
return root;
}
完整代码https://github.com/yhq915540781/javaPra/blob/master/src/Algorithm/dataStructure/BBSTree.java
参考文章
https://blog.csdn.net/isunbin/article/details/81707606