一.各种二叉树比较
1.满二叉树
一个二叉树,每层节点都能达到最大值,即若一颗高度为h的二叉树,其节点个数为2^h-1
2.完全二叉树
若二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树
3.平衡二叉树(AVL树)
在完全二叉树的基础上实现二叉排序树
二.问题引出
我们知道,数组元素创建二叉排序树时,数组第0个为根节点
例:int[] arr = {1,2,3,4,5,6};
这样构建的二叉排序树就是一个链表,这样会增加检索时间,降低效率
若把该二叉树转成AVL树则会大大提高检索效率
三.解决方案
将二叉排序树左旋转或右旋转,以达到该二叉排序树成为完全二叉树的特点
这里以该数组为例,右子树较高,故左旋
左旋步骤如下
1.创建一个新节点node,值为当前根节点的值
2.新节点的左子节点为根节点的左子节点
3.新节点的右子节点设置为根节点右子节点的左子节
4.根节点的值设为右子节点的值
5.根节点的右子节点设为右子节点的右子节点
6.根节点的左子节点设为新节点
class AVLTree{
public static int getHeight(TreeNode root){
//求以root为根节点树的高度
if(root == null)
return 0;
return Math.max(getHeight(root.left),getHeight(root.right))+1;
}
public static void Rotate(TreeNode root){
//旋转
//右大于左
if(getHeight(root.right)-getHeight(root.left)>1){
if(root.right!=null&&getHeight(root.right.left)>getHeight(root.right.right)){
rightRotate(root.right);
}
leftRotate(root);
}
//左大于右
else if(getHeight(root.left)-getHeight(root.right)>1){
if(root.left!=null&&getHeight(root.left.left)<getHeight(root.left.right)){
//提前旋转一次
leftRotate(root.left);
}
rightRotate(root);
}
}
public static void leftRotate(TreeNode root){//左旋
//1.创建一个新节点node,值为当前根节点的值
TreeNode node = new TreeNode(root.val);
//2.新节点的左子节点为根节点的左子节点
node.left = root.left;
//3.新节点的右子节点设置为根节点右子节点的左子节
node.right = root.right.left;
//4.根节点的值设为右子节点的值
root.val = root.right.val;
//5.根节点的右子节点设为右子节点的右子节点
root.right = root.right.right;
//6.根节点的左子节点设为新节点
root.left = node;
}
public static void rightRotate(TreeNode root){//右旋
TreeNode node = new TreeNode(root.val);
node.right = root.right;
node.left = root.left.right;
root.val = root.left.val;
root.left = root.left.left;
root.right = node;
}
}
class TreeNode{
int val;
TreeNode left;
TreeNode right;
TreeNode(){}
TreeNode(int val){
this.val = val;
}
}
二叉树是对称的,故右旋就是左旋的反过来
这里还有一个注意的点
在左旋和右旋之前,得判断一下根节点左子树和右子树的高度
以int[] arr = {10,11,7,6,8,9};为例
不管左旋还是右旋都无法形成AVL树
在右旋前应该判断左子树的左右子树,如果其右子树高度大于左子树的高度,则先将根节点的左子树进行左旋,然后再将整棵树右旋