代码较长,放在最后了
既然是平衡二叉树就离不开平衡因子的概念
每个节点都有一个平衡因子, 定义为int型 值是该节点的左子树高度减去右子树高度(有的地方是右-左,无所谓,自己保持一致就好)
如果是负的说明右子树高度高于左子树
平衡二叉树要求树中每个节点的平衡因子绝对值不大于1(即可以是 -1 0 1)
这里不介绍不平衡时具体的旋转方法, 只是列举我写的时候遇到的坑和要点,予以帮助掉到相同坑里的人
- 代码中大量使用了递归, 递归第一步就要写跳出条件, 照顾好递归的开头和结尾, 注意对根元素的判断, 根元素无父亲节点
- void 无返回值方法也可以使用return;提前跳出方法
- 平衡因子的更新是最难的点, 当插入节点后要判断父节点是否高度增加(也就是原来的平衡因子是否是0) ,当父元素的平衡因子改变父元素的父元素平衡因子不一定改变,也就是说判断是否向树根递归修改平衡因子的判断条件不是平衡因子是否改变, 而是高度是否增加, 如果插入后节点平衡因子更新后不等于0则递归向上修改(改后!=0说明高度增加了,影响其父节点的平衡因子)
- 判断旋转后修改区域内平衡因子的调整分几种情况, 可以参考这个大佬的分析, 有图示:https://blog.csdn.net/skyroben/article/details/72824146
- 旋转后和删除节点后对该区域上的父级元素平衡因子是否有影响的判定条件也是判断该子树高度是否改变, 但又和插入不同,需要判断该子树的平衡因子修改前和修改后的相对大小, 修改后比修改前更趋于0则向上递归(改前>0&&改后<改前)||(改前<0&&改后>改前)为真则修改其父元素的平衡因子
- 删除时也分为几类,网上的分析也很多, 写好判断调用插入时写好的旋转方法和平衡调节方法也不难, 注意删除一个叶子节点时不能直接在方法中把节点的引用直接赋值null, 因为这样会断开引用与对象的联系但节点对象还在二叉树上(父节点的指针还指向他), 具体原因是JAVA是值传递并非引用传递,方法操作的是引用的拷贝,删除叶子节点需要找到父节点, 把父节点指向它的引用设为null,这样就从树上删除了这个节点, 新手可能有疑问,怎么这就能直接赋null? 因为指向孩子的引用是对象的一个属性, 不是方法中传进来的, 传进来的是指向父节点的指针. 如果这样说还不理解 , 可以看下我的另一篇博客介绍JAVA参数的传递方式:https://blog.csdn.net/q5706503/article/details/82910428
代码是一个main类 一个节点Node类 一个平衡二叉树Tree类:
package treeAVL;
public class Node
{
private int number;
private int balance; //节点的平衡因子,设定为 左-右
private Node left,right,father;
Node(int num){this.number=num;}
public void setNumber(int num) {this.number=num;}
public int getNumber() {return this.number;}
public int getBalance() {return this.balance;}
public void riseBalance() {this.balance++;}
public void downBalance() {this.balance--;}
public Node getLeft() {return this.left;}
public void setLeft(Node node) {this.left=node;}
public Node getRight() {return this.right;}
public void setRight(Node node) {this.right=node;}
public Node getFather() {return this.father;}
public void setFather(Node node) {this.father=node;}
}
package treeAVL;
import javax.print.attribute.standard.RequestingUserName;
public class Tree {
private Node root;
//内部插入函数,递归用
private void innerInsert(Node newNode,Node rootNode)
{
if(newNode.getNumber()==rootNode.getNumber())
{
System.out.println("存在该数,插入失败!");
return;
}
if(newNode.getNumber()<rootNode.getNumber())
{
if(rootNode.getLeft()==null)
{
rootNode.setLeft(newNode);
newNode.setFather(rootNode);
setBanlance(newNode);
adjust(newNode);
System.out.println("插入成功!");
}
else
innerInsert(newNode,rootNode.getLeft());
}
else
{
if(rootNode.getRight()==null)
{
rootNode.setRight(newNode);
newNode.setFather(rootNode);
setBanlance(newNode);
adjust(newNode);
System.out.println("插入成功!");
}
else
innerInsert(newNode,rootNode.getRight());
}
}
//插入后更新平衡因子, 由node节点向上检查更新平衡因子
private void setBanlance(Node node)
{
//若已经到根节点则停止递归
if(node.getFather()==null)
{ return; }
if(node.getFather().getLeft()==node)
node.getFather().riseBalance();
else
node.getFather().downBalance();
//若该树高度增高了
if(node.getFather().getBalance()!=0)
setBanlance(node.getFather());
}
//旋转后或删除时平衡因子刷新函数, 以node的父亲开始向上调整 平衡因子
private void adjustBalanceToRoot(Node node)
{
if(node.getFather()==null)
{return;}
int before=node.getFather().getBalance();
if(node==node.getFather().getLeft())
node.getFather().downBalance();
else
node.getFather().riseBalance();
if((before>0&&node.getFather().getBalance()<before)||(before<0&&node.getFather().getBalance()>before))
adjustBalanceToRoot(node.getFather());
}
//以node为心左旋(提高右孩子)
private void rotateLeft(Node node)
{
if(node==this.root)
this.root=node.getRight();
Node temp=node.getRight().getLeft();
node.getRight().setFather(node.getFather());
if(node.getFather()!=null)
{
if(node==node.getFather().getRight())
node.getFather().setRight(node.getRight());
else
node.getFather().setLeft(node.getRight());
}
node.setFather(node.getRight());
node.getRight().setLeft(node);
node.setRight(temp);
if(temp!=null)
temp.setFather(node);
}
//以node为心右旋(提高左孩子)
private void rotateRight(Node node)
{
if(node==this.root)
this.root=node.getLeft();
Node temp=node.getLeft().getRight();
node.getLeft().setFather(node.getFather());
if(node.getFather()!=null)
{
if(node.getFather().getRight()==node)
node.getFather().setRight(node.getLeft());
else
node.getFather().setLeft(node.getLeft());
}
node.setFather(node.getLeft());
node.getLeft().setRight(node);
node.setLeft(temp);
if(temp!=null)
temp.setFather(node);
}
//向上递归找到第一个不平衡的节点的!儿子!, 若找到根则return null
private Node findFirstNotBanlance(Node node)
{
if(node==this.root)
return null;
if(node.getFather().getBalance()==2)
return node.getFather().getLeft();
else if(node.getFather().getBalance()==-2)
return node.getFather().getRight();
else
return findFirstNotBanlance(node.getFather());
}
//平衡调整,修复插入引起的不平衡,并更新平衡参数, 参数输入最新插入的节点
private void adjust(Node node)
{//向上找到第一个平衡因子=+-2的节点再旋转同时更新平衡因子的值
//定义一个变量储存向上找第一个不平衡节点的!儿子!,或根节点
Node sonOfNotBalance=findFirstNotBanlance(node);
//若该变量为空说明树平衡,无需处理
if(sonOfNotBalance==null)
return;
//该条件针对删除中的调整,该树高度不降低
if(sonOfNotBalance.getBalance()==0)
{
if(sonOfNotBalance.getFather().getBalance()==2)
{
rotateRight(sonOfNotBalance.getFather());
sonOfNotBalance.getRight().downBalance();
sonOfNotBalance.downBalance();
}
else
{
rotateLeft(sonOfNotBalance.getFather());
sonOfNotBalance.getLeft().riseBalance();
sonOfNotBalance.riseBalance();
}
return;
}
//分四类调整
if(sonOfNotBalance.getBalance()>0)
{
//右旋型
if(sonOfNotBalance.getFather().getLeft()==sonOfNotBalance)
{
rotateRight(sonOfNotBalance.getFather());
adjustBalanceToRoot(sonOfNotBalance);
sonOfNotBalance.downBalance();
sonOfNotBalance.getRight().downBalance();
sonOfNotBalance.getRight().downBalance();
}
//右左型
else
{
//判断类型,用于调节平衡因子
int type=sonOfNotBalance.getLeft().getBalance();
rotateRight(sonOfNotBalance);
rotateLeft(sonOfNotBalance.getFather().getFather());
if(type==0)
{
sonOfNotBalance.downBalance();
sonOfNotBalance.getFather().getLeft().riseBalance();
sonOfNotBalance.getFather().getLeft().riseBalance();
adjustBalanceToRoot(sonOfNotBalance.getFather());
}
else
{
if(type>0)
{
sonOfNotBalance.downBalance();
sonOfNotBalance.downBalance();
adjustBalanceToRoot(sonOfNotBalance.getFather());
sonOfNotBalance.getFather().downBalance();
sonOfNotBalance.getFather().getLeft().riseBalance();
sonOfNotBalance.getFather().getLeft().riseBalance();
}
else
{
sonOfNotBalance.downBalance();
adjustBalanceToRoot(sonOfNotBalance.getFather());
sonOfNotBalance.getFather().riseBalance();
sonOfNotBalance.getFather().getLeft().riseBalance();
sonOfNotBalance.getFather().getLeft().riseBalance();
sonOfNotBalance.getFather().getLeft().riseBalance();
}
}
}
}
else
{
//左旋型
if(sonOfNotBalance.getFather().getRight()==sonOfNotBalance)
{
rotateLeft(sonOfNotBalance.getFather());
adjustBalanceToRoot(sonOfNotBalance);
sonOfNotBalance.riseBalance();
sonOfNotBalance.getLeft().riseBalance();
sonOfNotBalance.getLeft().riseBalance();
}
//左右型
else
{
int type=sonOfNotBalance.getRight().getBalance();
rotateLeft(sonOfNotBalance);
rotateRight(sonOfNotBalance.getFather().getFather());
if(type==0)
{
sonOfNotBalance.riseBalance();
sonOfNotBalance.getFather().getRight().downBalance();
sonOfNotBalance.getFather().getRight().downBalance();
adjustBalanceToRoot(sonOfNotBalance.getFather());
}
else
{
if (type<0)
{
sonOfNotBalance.riseBalance();
sonOfNotBalance.riseBalance();
adjustBalanceToRoot(sonOfNotBalance.getFather());
sonOfNotBalance.getFather().riseBalance();
sonOfNotBalance.getFather().getRight().downBalance();
sonOfNotBalance.getFather().getRight().downBalance();
}
else
{
sonOfNotBalance.riseBalance();
adjustBalanceToRoot(sonOfNotBalance.getFather());
sonOfNotBalance.getFather().downBalance();
sonOfNotBalance.getFather().getRight().downBalance();
sonOfNotBalance.getFather().getRight().downBalance();
sonOfNotBalance.getFather().getRight().downBalance();
}
}
}
}
}
//先序遍历输出树的数字
private void show1(Node node)
{
System.out.print(node.getNumber()+"+"+node.getBalance());
System.out.print(" ");
if(node.getLeft()!=null)
{
show1(node.getLeft());
}
if(node.getRight()!=null)
{
show1(node.getRight());
}
}
//找到该数,返回节点对象
private Node find(int num,Node rootNode)
{
if(rootNode==null)
return null;
if(rootNode.getNumber()==num)
return rootNode;
else
{
if(num<rootNode.getNumber())
return find(num,rootNode.getLeft());
else
return find(num,rootNode.getRight());
}
}
//删除node函数,调整平衡因子,针对叶子节点
private void deleLeaf(Node node)
{
if(node==this.root)
this.root=null;
else
{
if(node==node.getFather().getLeft())
{
if(node.getFather().getRight()!=null)
node.getFather().downBalance();
else
adjustBalanceToRoot(node);
node.getFather().setLeft(null);
}
else
{
if(node.getFather().getLeft()!=null)
node.getFather().riseBalance();
else
adjustBalanceToRoot(node);
node.getFather().setRight(null);
}
}
}
//删除节点,调整平衡因子,针对只有一个儿子的节点
private void deleOneSon(Node node)
{
Node son;
//只有右孩子
if(node.getLeft()==null)
son=node.getRight();
//只有左孩子
else
son=node.getLeft();
if(node==this.root)
{
this.root=son;
return;
}
son.setFather(node.getFather());
if(node.getFather().getRight()==node)
node.getFather().setRight(son);
else
node.getFather().setLeft(son);
adjustBalanceToRoot(son);
}
//交换数据,指针平衡因子等不动
private void exchange(Node node1,Node node2)
{
int temp=node1.getNumber();
node1.setNumber(node2.getNumber());
node2.setNumber(temp);
}
//返回以rootNode为根的最大值(包括本身)
private Node max(Node rootNode)
{
if(rootNode.getRight()==null)
return rootNode;
else
return max(rootNode.getRight());
}
//返回最小值
private Node min(Node rootNode)
{
return (rootNode.getLeft()==null?rootNode:min(rootNode.getLeft()));
}
//-----------以上是私有方法,属性------------------------
//插入函数,若树中已有该数则输出错误
public void insert(int num)
{
Node node=new Node(num);
//如果根节点为空 赋给根节点后跳出
if(this.root==null)
{
this.root=node;
System.out.println("插入成功!");
return;
}
innerInsert(node,this.root);
}
//删除函数,若树中没有该树则报错
public void delete(int num)
{
Node node=find(num,this.root);
if(node==null)
{
System.out.println("该数不存在,删除失败!");
return;
}
//若是叶子节点直接删除
if(node.getRight()==null&&node.getLeft()==null)
{
deleLeaf(node);
adjust(node);
System.out.println("删除成功");
}
//该节点只有右儿子
else if(node.getLeft()==null&&node.getRight()!=null)
{
deleOneSon(node);
adjust(node);
System.out.println("删除成功");
}
//只有左儿子
else if(node.getLeft()!=null&&node.getRight()==null)
{
deleOneSon(node);
adjust(node);
System.out.println("删除成功");
}
//有两个儿子
else
{
Node substitude;
if(node.getBalance()==0||node.getBalance()==1)
{
substitude=max(node.getLeft());
exchange(node,substitude);
//如果替换节点是叶子节点
if(substitude.getLeft()==null)
{
deleLeaf(substitude);
adjust(substitude);
System.out.println("删除成功");
}
//如果只有左子树
else
{
deleOneSon(substitude);
adjust(substitude);
System.out.println("删除成功");
}
}
else
{
substitude=min(node.getRight());
exchange(node,substitude);
if(substitude.getRight()==null)
{
deleLeaf(substitude);
adjust(substitude);
System.out.println("删除成功");
}
else
{
deleOneSon(substitude);
adjust(substitude);
System.out.println("删除成功");
}
}
}
}
public void show()
{
show1(this.root);
}
}
package treeAVL;
public class Main {
public static void main(String[] args) {
Tree tree=new Tree();
tree.insert(1);
tree.insert(2);
tree.insert(8);
tree.insert(3);
tree.insert(4);
tree.insert(5);
tree.insert(-1);
tree.insert(-5);
tree.insert(-2);
tree.insert(9);
tree.insert(0);
tree.insert(6);
tree.insert(50);
tree.insert(39);
tree.insert(19);
tree.insert(14);
tree.insert(26);
tree.insert(27);
tree.delete(27);
tree.delete(39);
tree.delete(-1);
tree.delete(-5);
tree.delete(3);
tree.delete(0);
tree.delete(-2);
tree.delete(26);
tree.show();
}
}
代码很长 , 有很多可以优化的地方,新手的代码很烂, 欢迎批评!