平衡二叉树

前言:

平衡二叉树是基于二叉排序树(二叉搜索树)形成的,二叉查找树的前提是数据是有序的。

 假如我要查找7这个值,那我需要遍历3次,也就是树的深度,每遍历一层,数据就减少一半,所以查找的时间复杂度O(logn)

但右边种情况就让查找的时间复杂度退化到了O(n):

     

为了解决这个问题,平衡二叉树诞生了,也叫AVL树。。

一、基本定义和性质

1.1、定义:


        平衡二叉树也叫AVL树,它或者是一颗空树,或者具有以下性质的二叉排序树:它的左子树和左子树的高度之差(平衡因子)的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树。

1.2、平衡二叉树的性质

  • 左子树和右子树的高度之差的绝对值小于等于1
  • 左子树和右子树也是平衡二叉树

1.3、平衡因子(BF)与判断平衡二叉树

        平衡因子  =  结点左子树的高度  -  结点右子树的高度。

       

        以A节点为例,它的左边树的高度left_height = 1,右边树的高度right_height = 2,高度差left_height - right_height = -1,如果高度差绝对值不超过1,那么说明该树就是平衡的。

                         不平衡如何解决?

       

二、平衡二叉树的旋转

2.1  LL型(右旋)

        右旋就是通过顺时针旋转来实现平衡,就是处于右边的根结点落下变为叶子节点,而中间的节点变为新的根节点,最左边的叶子节点保持不变,最后形成新的结构。

                                             

    

2.2  RR型(左旋)

        对于RR型失衡,通过逆时针旋转,最左边的根结点落下,变成叶子节点,中间的节点变为新的根节点,最右边的叶子节点保持不变。

                                     

   

2.3  LR型(先左后右)

对于LR型失衡,先在根节点左子树进行左旋处理,变成LL型失衡,然后再去通过右旋来实现整体平衡。动图演示如下:

                                             

2.4  RL型(先右后左)

对于RL型失衡,在根节点的右子树先进行右旋处理,形成RR型失衡,然后整体进行左旋处理,最后达到平衡状态,动态演示如下:

                                             

   

三、平衡二叉树代码实现

3.1构建平衡二叉树

插入节点思路:

       1、 找到插入位置:根据二叉搜索树的性质,从根节点开始,比较关键字,决定向左子树或右子树继续查找,直到找到适合插入的位置。
        2、插入节点:在找到的空位置创建新节点并插入。
        3、更新节点高度:在插入新节点后,更新从新节点到根节点的所有节点的高度。
        4、检查平衡因子:计算当前节点的平衡因子,以确定是否需要进行旋转操作。
        5、进行旋转:根据平衡因子的值(大于 1 或小于 -1)决定进行左旋、右旋、左-右旋或右-左旋,以保持 AVL 树的平衡。

1.创建节点
//创建一个树的节点类
public class AVLNode {
    //保存节点的key值
    int key;
    //保存节点的高度
    int height;
    //节点的左孩子和右孩子
    AVLNode left, right;

    //有参构造
    public AVLNode(int key) {
        this.key = key;
        this.height = 1; // 新节点的初始高度为 1
    }
}
2.创建二叉树即方法
public class AVLTree {

    //根节点
    private AVLNode root;

    // 插入接口
    public void insert(int key) {
        root = insert(root, key);
    }

    // 删除接口
    public void delete(int key) {
        root = delete(root, key);
    }

    // 获取树的根节点
    public AVLNode getRoot() {
        return root;
    }


    // 中序遍历接口
    public void inOrder() {
        inOrder(root);
    }
}
3.获取节点高度
// 获取节点高度
    private int height(AVLNode node) {
        //当节点为空时返回0
        if (node == null){
            return 0;
        }else{//节点不为空时返回节点高度
            return node.height;
        }
    }
4.获取平衡因子
    // 获取平衡因子
    private int getBalance(AVLNode node) {
        //当节点为空时,返回0
        if (node == null){
            return 0;
        }else {//节点不为空时返回左孩子高度-右孩子高度
            return (height(node.left)-height(node.right));
        }
    }
5.插入节点
// 插入节点
    public AVLNode insert(AVLNode node, int key) {
        // 1. 执行标准的 BST 插入
        if (node == null) {
            return new AVLNode(key);
        }

        if (key < node.key) {
            node.left = insert(node.left, key);
        } else if (key > node.key) {
            node.right = insert(node.right, key);
        } else { // 重复的键不允许
            return node;
        }

        // 2. 更新节点的高度
        node.height = Math.max(height(node.left), height(node.right)) + 1;

        // 3. 检查节点的平衡因子
        int balance = getBalance(node);

        // 如果节点不平衡,则进行旋转

        // 左左情况
        if (balance > 1 && key < node.left.key) {
            return rightRotate(node);
        }

        // 右右情况
        if (balance < -1 && key > node.right.key) {
            return leftRotate(node);
        }

        // 左右情况
        if (balance > 1 && key > node.left.key) {
            node.left = leftRotate(node.left);
            return rightRotate(node);
        }

        // 右左情况
        if (balance < -1 && key < node.right.key) {
            node.right = rightRotate(node.right);
            return leftRotate(node);
        }

        // 返回更新后的节点
        return node;
    }
6.左旋转
// 左旋转
    private AVLNode leftRotate(AVLNode x) {
        AVLNode y = x.right;
        AVLNode T2 = y.left;

        // 旋转
        y.left = x;
        x.right = T2;

        // 更新高度
        x.height = Math.max(height(x.left), height(x.right)) + 1;
        y.height = Math.max(height(y.left), height(y.right)) + 1;

        // 返回新的根节点
        return y;
    }
7.右旋转
// 右旋转
    private AVLNode rightRotate(AVLNode y) {

        //记录节点位置
        AVLNode x = y.left;
        AVLNode T2 = x.right;

        // 旋转
        x.right = y;
        y.left = T2;

        // 更新高度
        y.height = Math.max(height(y.left), height(y.right)) + 1;
        x.height = Math.max(height(x.left), height(x.right)) + 1;

        // 返回新的根节点
        return x;
    }
8.中序遍历
    // 中序遍历
    public void inOrder(AVLNode node) {
        if (node != null) {
            inOrder(node.left);
            System.out.print(node.key + " ");
            inOrder(node.right);
        }
    }

3.2 删除二叉树节点

删除节点的思路:

1、找到要删除的节点:根据二叉搜索树的性质,比较关键字,决定向左子树或右子树继续查找。


2、删除节点:
        2.1、如果节点没有子节点,直接删除。
        2.2、如果节点只有一个子节点,将子节点连接到其父节点。
        2.3、如果节点有两个子节点,找到右子树的最小值(或左子树的最大值),将其复制到当前节点,然后递归删除该最小值(或最大值)。


3、更新节点的高度:在节点删除后,更新当前节点的高度。


4、检查平衡因子:计算当前节点的平衡因子,以确定是否需要进行旋转操作。


5、进行旋转:根据平衡因子的值(大于 1 或小于 -1)决定进行左旋、右旋、左-右旋或右-左旋。

1.找到右子树最小值
 // 找到右子树的最小值
    private AVLNode minValueNode(AVLNode node) {
        AVLNode current = node;
        while (current.left != null) {
            current = current.left;
        }
        return current;
    }
2.删除节点
// 删除节点
    public AVLNode delete(AVLNode root, int key) {
        // 执行标准的 BST 删除
        if (root == null) {
            return root;
        }

        if (key < root.key) {
            root.left = delete(root.left, key);
        } else if (key > root.key) {
            root.right = delete(root.right, key);
        } else {
            // 节点只有一个子节点或没有子节点
            if ((root.left == null) || (root.right == null)) {
                AVLNode temp = root.left != null ? root.left : root.right;

                // 如果没有子节点
                if (temp == null) {
                    return null;
                } else { // 只有一个子节点
                    return temp;
                }
            } else {
                // 节点有两个子节点,找到右子树的最小值
                AVLNode temp = minValueNode(root.right);
                root.key = temp.key; // 复制最小值到当前节点
                root.right = delete(root.right, temp.key); // 删除最小值
            }
        }

        // 如果树只有一个节点,则返回
        if (root == null) {
            return root;
        }

        // 更新节点的高度
        root.height = Math.max(height(root.left), height(root.right)) + 1;

        // 检查节点的平衡因子
        int balance = getBalance(root);

        // 如果节点不平衡,则进行旋转

        // 左左情况
        if (balance > 1 && getBalance(root.left) >= 0) {
            return rightRotate(root);
        }

        // 左右情况
        if (balance > 1 && getBalance(root.left) < 0) {
            root.left = leftRotate(root.left);
            return rightRotate(root);
        }

        // 右右情况
        if (balance < -1 && getBalance(root.right) <= 0) {
            return leftRotate(root);
        }

        // 右左情况
        if (balance < -1 && getBalance(root.right) > 0) {
            root.right = rightRotate(root.right);
            return leftRotate(root);
        }

        // 返回更新后的根节点
        return root;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值