为啥要有avl树
avl树是在二叉搜索树下的一种进阶形式,是为了防止二叉搜索树在极端情况下产生的链表化的场景,从而在二叉搜索树的基础上,加上了某些条件来阻止这种极端情况的产生,但不是保证完全平衡,而是放开了一定的条件,使得这种情况不那么难以满足.(条件:左右子树的高度差的绝对值不大于1) ,我们在发现大于1的时候可以使用左右旋转的方式来调整数的形态,从而保证了查找的时候有近似于O(logN)的性能.
缺点:
当然,有得必有失,这样也带来了一定的损耗:浪费了空间来保存新的变量,每次插入都判断是否满足条件,这样导致了插入的效率变低,这也使得这种二叉树不适合连续多次的插入和修改数据.
如果我们需要持续多次的插入数据,那么也有更进一步的数据结构 --- 红黑树
AVL树的定义
平衡因子
定义为每个节点的左子树和右子树的高度差(左减去右,右减去左都可以表示)
本文使用右减左表示高度差的定义
例如空树的高度差就是0,这就代表着平衡,
当高度差达到-1,就表示左子树高了,但是还在允许的范围之间,无需调整
当高度差达到1,就代表右子树高了,也在允许的范围之内
当左右子树的高度差超过这个范围(绝对值大于2的时候),那么我们就得需要实现左右旋转来满足这种结构的查找效率.
节点代码结构
static class TreeNode{ public int val; public int bf;//balance factor 平衡因子 public TreeNode left;//左孩子 public TreeNode right;//右孩子 public TreeNode parent;//父节点 public TreeNode(int val) { this.val = val; } }
实现插入功能
总体思路:
1.按照平衡二叉树的比较方式去寻找到应该插入在的节点
2.插入完了修改一下bf(平衡因子)的值
3.判断是否平衡
3.1 bf = 0直接跳出循环即可
3.2 bf = 1 或 bf = -1就继续向上寻找
3.3 bf = 2考虑左旋右旋修改avl树的结构
以下会包含四种失衡以及解决方案
1.LL类型
我们能很容易发现这种不平衡式因为左子树太深而导致的,此时我们要使用右旋来保证结构的正确.
具体步骤就是
1.将subL作为根节点
2.subLR作为parent的左孩子
3.parent作为subL的右孩子
此时我们发现subL和parent的平衡因子都会变成0
2.RR类型
此时我们明显发现右子树的深度要高于左子树两个节点
此时我们就需要进行左旋来调整整体结构的状态
整体步骤与之前类似
1.将subR提出来当根节点
2.subRL作为parent的右子树
3.parent作为subR的左子树
3.RL类型
这个你可以理解是在右子树深的情况下子树是左子树比较深,而前面的方式是左右子树的子树也是呈现同一趋势的.
执行方式
1.先对右子树进行一次右旋,这时候我们发现总体又一次呈现了RR型状态
左旋即可达成平衡的目标
这里如果我们插入的是节点值是55又会有不一样的平衡值
4.LR类型
执行方式也是一样的,我们对左子树进行左旋,再对整体进行右旋即可
这里我直接给出操作形式,其实这里插入的是25也会有另一种结果,在代码中有所体现,读者可自行画图推导
完整avl树插入代码
public class AVLTree { static class TreeNode{ public int val; public int bf;//balance factor 平衡因子 public TreeNode left;//左孩子 public TreeNode right;//右孩子 public TreeNode parent;//父节点 public TreeNode(int val) { this.val = val; } } public TreeNode root;//根节点 public boolean insert(int val){ TreeNode node = new TreeNode(val); if(root == null) { root = node; return true; } TreeNode cur = root; TreeNode parent = null; while(cur != null){ if(val < cur.val){ parent = cur; cur = cur.left; }else if(val == cur.val){ return false; }else{ parent = cur; cur = cur.right; } } if(parent.val>val){ parent.left = node; }else{ parent.right = node; } node.parent = parent; //调节负载因子 cur = node; //负载因子的修改 while(parent != null){ //先看cur是parent的左还是右 //平衡因子为右 - 左 if(cur == parent.right){ //右树高,因子++ parent.bf++; }else{ //左树高 parent.bf--; } //检查平衡因子变化完了是多少 if(parent.bf == 0){ break; }else if(parent.bf == 1 || parent.bf == -1){ //继续向上判断修改平衡因子 //因为等于1和-1只代表当前子树平衡 cur = parent; parent = parent.parent; }else{ //2或者-2的情况,考虑左旋右旋 if(parent.bf == 2){ if(cur.bf == 1){ rotateLeft(parent); }else{ //-1的情况 rotateRL(parent); } }else{ //-2的情况 if(cur.bf == 1){ rotateLR(parent); }else{ //-1的情况 rotateRight(parent); } } break; } } return true; } //左单旋 private void rotateLeft(TreeNode parent) { TreeNode subR = parent.right; TreeNode subRL = subR.left; parent.right = subRL; subR.left = parent; if(subRL != null){ subRL.parent = parent; } TreeNode pParent = parent.parent; parent.parent = subR; if(root == parent){ root = subR; root.parent = null; }else{ if(pParent.left == parent){ pParent.left = subR; }else{ pParent.right = subR; } subR.parent = pParent; } subR.bf = parent.bf = 0; } //右单旋 private void rotateRight(TreeNode parent){ //画图便于理解 TreeNode subL = parent.left; TreeNode subLR = subL.right; parent.left = subLR; subL.right = parent; if(subLR != null){ subLR.parent = parent; } //必须先记录 TreeNode pParent = parent.parent; parent.parent = subL; //检查当前是否为根节点 if(parent == root){ root = subL; root.parent = null; }else{ //此刻的parent也不一定是根节点,还是有左右树的 if(pParent.left == parent){ pParent.left = subL; }else{ pParent.right = subL; } subL.parent = pParent; } subL.bf = 0; parent.bf = 0; } //左右双旋 private void rotateLR(TreeNode parent){ TreeNode subL = parent.left; TreeNode subLR = subL.right; int bf = subLR.bf; rotateLeft(parent.left); rotateRight(parent); if(bf == -1){ subL.bf = 0; subLR.bf = 0; parent.bf = 1; }else if(bf == 1){ subL.bf = -1; subLR.bf = 0; parent.bf = 0; } } //右左双旋 private void rotateRL(TreeNode parent) { TreeNode subR = parent.right; TreeNode subRL = subR.left; int bf = subRL.bf; rotateRight(parent.right); rotateLeft(parent); if(bf == 1){ parent.bf = -1; subR.bf = 0; subRL.bf = 0; }else if(bf == -1){ parent.bf = 0; subR.bf = 0; subRL.bf = 1; } } //验证当前树 public void inorder(TreeNode root){ if(root == null){ return; } inorder(root.left); System.out.println(root.val+" "); inorder(root.right); } private int height(TreeNode root){ if(root == null){ return 0; } int left = height(root.left); int right = height(root.right); return Math.max(left+1,right+1); } public boolean isBalanced(TreeNode root){ if(root == null){ return true; } int left = height(root.left); int right = height(root.right); //判断平衡因子是否有问题 if (right-left != root.bf){ System.out.println("这个节点" + root.val +"平衡因子异常"); return false; } return Math.abs(left-right)<=1&& isBalanced(root.left) && isBalanced(root.right); } public static void main(String[] args) { int[] arr= new int[]{4, 2, 6, 1, 3, 5, 15, 7, 16,14}; AVLTree avl = new AVLTree(); for (int i = 0; i < arr.length; i++) { avl.insert(arr[i]); } System.out.println(avl.isBalanced(avl.root)); } }