Avl树梳理

在这里插入图片描述

AVL树

总所周知二叉搜索树会出现一些极端情况,影响查询效率。
为了解决这个问题,就需要把对极端的情况进行调整,优化查询, 比如:
在这里插入图片描述
为了实现右图,就需要增加一个旋转功能,我们那它命名为旋转,向右转叫做右旋,向左就叫左旋。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yWkZ1oHd-1628952987189)(AVL%E6%A0%91%20377d4c10d2474ae1854ad0094429a98c/Untitled%201.png)]

public avlNode leftRate(avlNode node) {
    avlNode right = node.right;
    node.right = right.left;
    right.left = node;
    return right;
}

这时候就出现了两个问题

  1. 什么时候该旋转?
  2. 如何判定该往哪个方向转?
问题一:什么时候该旋转?

我们可以找到一个最小且极端情况的二叉搜索树作为参考:

下图发生了左旋,b节点成为根,a节点下降成子节点,3层树变成了2层,所以每个节点隐含了高度属性。
h1表示高度1,h3表示高度3,h0表示空节点,高度为0
根据图中得出 a.左.h【a的左子节点高度】 - a.右.h【b的高度】 > 1
在这里插入图片描述

int cha = cur.left.height - cur.right.height;
if (Math.abs(cha) <= 1) {
    return cur;
}
问题二:如何判定该往哪个方向转?

在即 a.左.h - a.右.h > 1 的基础上,枚举出所有情况如下:

  1. 情况1需要左旋:判定条件,a.右.右.h - a.右.左.h > 0
  2. 情况2需要先右旋再左旋:判定条件,a.右.左.h - a.右.右.h > 0
  3. 情况1需要左旋:判定条件,a.左.右.h - a.左.左.h > 0
  4. 情况1需要先左旋再右旋:判定条件,a.左.右.h - a.左.左.h > 0
private avlNode<K, V> maintain(avlNode<K, V> cur) {
  if (cur == null) {
      return null;
  }
  int leftHeight = cur.left != null ? cur.left.height : 0;
  int rightHeight = cur.right != null ? cur.right.height : 0;
  if (Math.abs(leftHeight - rightHeight) > 1) {
      if (leftHeight > rightHeight) {
          int leftLeftHeight = cur.left != null && cur.left.left != null ? cur.left.right.height : 0;
          int leftRightHeight = cur.left != null && cur.left.right != null ? cur.left.right.height : 0;
          if (leftLeftHeight >= leftRightHeight) {
              cur = rightRotate(cur);
          } else {
              cur.left = leftRotate(cur.left);
              cur = rightRotate(cur);
          }
      } else {
          int rightLeftHeight = cur.right != null && cur.right.left != null ? cur.right.left.height : 0;
          int rightRightHeight = cur.right != null && cur.right.right != null ? cur.right.right.height : 0;
          if (rightRightHeight >= rightLeftHeight) {
              cur = leftRotate(cur);
          } else {
              cur.right = rightRotate(cur.right);
              cur = leftRotate(cur);
          }
      }
  }
  return cur;
}
问题三:节点增加了高度属性,高度如何维护?

a.h = max(a.左.h, a.右.h) + 1

  1. 添加节点,父节点高度要重新计算且是递归

    public avlNode<K, V> add(avlNode<K, V> cur, K key, V value) {
        if (cur == null) {
            return new avlNode<K, V>(key, value);
        }
        if (key.compareTo(cur.key) < 0) {
            cur.left = add(cur.left, key, value);
        } else {
            cur.right = add(cur.right, key, value);
        }
        // 重新计算高度
        cur.height = Math.max(cur.left != null ? cur.left.height : 0, cur.right != null ? cur.right.height : 0) + 1;
        return maintain(cur);
    }
    
  2. 删除节点,父节点高度也要重新计算,一样是递归

    public avlNode<K, V> delete(avlNode<K, V> cur, K key) {
        if (key.compareTo(cur.key) > 0) {
            cur.right = delete(cur.right, key);
        } else if (key.compareTo(cur.key) < 0) {
            cur.left = delete(cur.left, key);
        } else {
            if (cur.left == null && cur.right == null) {
                // 叶子节点直接删除
                cur = null;
            } else {
                // 非叶子节点
                if (cur.left != null && cur.right != null) {
                    // 双子节点, 找到最接近 当前节点的叶子节点
                    // 使用该节点替换要删除的节点
                    avlNode<K, V> des = cur.right;
                    while (des.left != null) {
                        des = des.left;
                    }
                    cur.right = delete(cur.right, des.key);
                    des.left = cur.left;
                    des.left = cur.right;
                    cur = des;
                } else {
                    // 单子节点,子节点替换当前节点
                    cur = cur.left != null ? cur.left : cur.right;
                }
            }
        }
        if (cur != null) {
            cur.height = Math.max(cur.left != null ? cur.left.height : 0, cur.right != null ? cur.right.height : 0) + 1;
        }
        return maintain(cur);
    }
    
  3. 1和2完成后,平衡被破坏的情况下,所以旋转操作的都要重新计算一遍节点高度

    private avlNode<K, V> leftRotate(avlNode<K, V> cur) {
        avlNode<K, V> right = cur.right;
        cur.right = right.left;
        right.left = cur;
        cur.height = Math.max((cur.left != null ? cur.left.height : 0), (cur.right != null ? cur.right.height : 0)) + 1;
        right.height = Math.max((right.left != null ? right.left.height : 0), (right.right != null ? right.right.height : 0)) + 1;
        return right;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值