Java-平衡二叉树-详细注释-无原理


/** @author syh17 参考 https://blog.csdn.net/isunbin/article/details/81707606 */
public class AvlTree {

  /**
   * 获取树高
   *
   * @param root 获取树高的节点
   * @return 树的高度
   */
  public static int getHeight(Node root) {
    if (root == null) {
      return 0;
    }
    return root.height;
  }

  /**
   * 更新树的高度
   *
   * @param root 需要更新树高的节点
   */
  public static void updateHeight(Node root) {
    root.height = 1 + Math.max(getHeight(root.left), getHeight(root.right));
  }

  /**
   * ll型旋转
   *
   * @param root 需要旋转的节点
   * @return 旋转后的节点
   */
  public static Node llX(Node root) {
    Node temp = root.left;
    root.left = temp.right;
    temp.right = root;
    root.height = 1 + Math.max(getHeight(root.left), getHeight(root.right));
    temp.height = 1 + Math.max(getHeight(temp.left), getHeight(temp.right));
    return temp;
  }

  /**
   * rr型旋转
   *
   * @param root 需要旋转的节点
   * @return 旋转后的节点
   */
  public static Node rrX(Node root) {
    Node temp = root.right;
    root.right = temp.left;
    temp.left = root;
    root.height = 1 + Math.max(getHeight(root.left), getHeight(root.right));
    temp.height = 1 + Math.max(getHeight(temp.left), getHeight(temp.right));
    return temp;
  }

  /**
   * lr型旋转
   *
   * @param root 需要旋转的节点
   * @return 旋转后的节点
   */
  public static Node lrX(Node root) {
    root.left = rrX(root.left);
    return llX(root);
  }

  /**
   * rl型旋转
   *
   * @param root 需要旋转的节点
   * @return 旋转后的节点
   */
  public static Node rlX(Node root) {
    root.right = llX(root.right);
    return rrX(root);
  }

  /**
   * 向平衡二叉树插入数据
   *
   * @param root 插入二叉树的根节点
   * @param key 插入的值
   * @return 插入后的根节点
   */
  public static Node insert(Node root, int key) {
    // 如果节点是空 则直接新建一个节点返回
    if (root == null) {
      return new Node(key);
    }

    if (key < root.value) {
      // 如果key小于当前节点, 则添加至其左子树
      root.left = insert(root.left, key);
    } else if (key > root.value) {
      // 如果key大于当前节点, 则添加至其右子树
      root.right = insert(root.right, key);
    } else {
      // key与当前值相等, 则直接返回此节点
      return root;
    }

    // 更新树高
    updateHeight(root);

    // 获取两个子树的高度差
    int difference = getDifference(root);

    // 左子树过高且key小于左节点的值  说明需要ll旋转
    if (difference > 1 && key < root.left.value) {
      return llX(root);
    }

    // 右子树过高且key大于右节点的值  说明需要rr旋转
    if (difference < -1 && key > root.right.value) {
      return rrX(root);
    }

    // 左子树过高且key大于左节点的值  说明需要lr旋转
    if (difference > 1 && key > root.left.value) {
      return lrX(root);
    }

    // 右子树过高且key小于右节点的值  说明需要rl旋转
    if (difference < -1 && key < root.right.value) {
      return rlX(root);
    }
    return root;
  }

  /**
   * 获取此树中的最大值
   *
   * @param root 根节点
   * @return 当前树中的最大值
   */
  public static int getMaxValue(Node root) {
    Node current = root;
    while (current.right != null) {
      current = current.right;
    }
    return current.value;
  }

  /**
   * 获取左子树高度与右子树高度的差值
   *
   * @param root 判断差值的根节点
   * @return 左子树高度与右子树高度的差值
   */
  public static int getDifference(Node root) {
    return getHeight(root.left) - getHeight(root.right);
  }

  /**
   * 删除节点
   *
   * @param root 需要删除节点的树的根节点
   * @param key 需要删除的key
   * @return 删除后的根节点
   */
  public static Node deleteNode(Node root, int key) {
    if (root == null) {
      return null;
    }
    if (key < root.value) {
      root.left = deleteNode(root.left, key);
    } else if (key > root.value) {
      root.right = deleteNode(root.right, key);
    } else {
      // 只有零个或一个子节点
      if ((root.left == null) || (root.right == null)) {
        // 如果有一个不为空 则可以获取到不为空的节点
        Node temp = root.left == null ? root.right : root.left;

        // 如果获取到了不为空的子节点, 则将子节点的值赋值给当前节点, 相当于删除这个子节点
        if (temp != null) {
          root.value = temp.value;
          root.left = temp.left;
          root.right = temp.right;
        } else {
          // 没有子节点, 说明是叶子节点 直接删除
          return null;
        }
      } else {
        // 如果两个子节点都存在, 取左子树的最大值作为root节点的值, 这样不会破坏平衡二叉搜索树的性质
        root.value = getMaxValue(root.left);
        // 删除左子树中的最大值, 因为左子树的最大值已经作为root的值了, 需要删除
        root.left = deleteNode(root.left, root.value);
      }
    }

    // 更新树高
    updateHeight(root);

    // 判断旋转
    int difference = getDifference(root);
    // 当前树的左子树高, 并且左子树的左子树高或者相等 LL型
    if (difference > 1 && getDifference(root.left) >= 0) {
      return llX(root);
    }
    // 当前树的左子树高, 并且左子树的右子树高 LR型
    if (difference > 1 && getDifference(root.left) < 0) {
      return lrX(root);
    }
    // 当前树的右子树高, 并且右子树的右子树高或者相等 RR型
    if (difference < -1 && getDifference(root.right) <= 0) {
      return rrX(root);
    }
    // 当前树的右子树高, 并且右子树的左子树高 Rl型
    if (difference < -1 && getDifference(root.right) > 0) {
      return rlX(root);
    }
    // 当前还是平衡的 不旋转
    return root;
  }

  /**
   * 中序遍历
   *
   * @param root 需要遍历的节点
   */
  public static void zNode(Node root) {
    if (root == null) {
      return;
    }
    zNode(root.left);
    System.out.println(root.value);
    zNode(root.right);
  }

  /**
   * 前序遍历
   *
   * @param root 需要遍历的节点
   */
  public static void preNode(Node root) {
    if (root == null) {
      return;
    }
    System.out.println(root.value);
    preNode(root.left);
    preNode(root.right);
  }

  public static void main(String[] args) {
    Node root = null;
    root = insert(root, 2);
    root = insert(root, 1);
    root = insert(root, 0);
    root = insert(root, 3);
    root = insert(root, 4);
    root = insert(root, 4);
    root = insert(root, 5);
    root = insert(root, 6);
    root = insert(root, 9);
    root = insert(root, 8);
    root = insert(root, 7);

    zNode(root);

    deleteNode(root, 5);
    System.out.println("-------------");
    preNode(root);
    System.out.println("-------------");
    deleteNode(root, 4);

    System.out.println("-------------");
    preNode(root);
    System.out.println("-------------");

    zNode(root);
  }

  /** 树节点的定义 */
  private static class Node {
    private int value;
    private Node left;
    private Node right;
    private int height;

    public Node(int value) {
      this.value = value;
      this.height = 1;
    }
  }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值