二叉平衡树(左单旋,右单旋,左右双旋、右左双旋)

一、AVL树(二叉平衡树:高度平衡的二叉搜索树)

0、二叉平衡树

左右子树高度差不超过1的二叉搜索树。

public class AVLTree{
    static class AVLTreeNode {
        public TreeNode left = null; // 节点的左孩子
        public TreeNode right = null; // 节点的右孩子
        public TreeNode parent = null; // 节点的双亲
        public int val = 0;
        public int bf = 0; // 当前节点的平衡因子=右子树高度-左子树的高度
        
        public TreeNode(int val) {
            this.val = val;
        }
    }
    public TreeNode root;

    //插入函数等....
    
}
// 将AVLTreeNode定义为AVLTree的静态内部类

1、查找

二叉平衡树的查找和二叉搜索树的方法是一样的,因为它们具有相同的结构特点——右孩子val值小于根节点val,根节点val小于左孩子val。

2、插入

二叉搜索树的插入一定是插入到叶子节点的位置。
二叉平衡树将节点插入到叶子节点之后,要维护左右子树的平衡因此可能还要进行旋转操作。

  1. 先将数据插入到AVL树当中(和二叉搜索数一样)
  2. 插入进去后,根据平衡因子来进行对树的调整

3、插入后无需旋转的情况:

注意各节点bf值得变化

image.png

4、右单旋&左单旋:

对parent进行右单旋就是把parent.left提拔成根节点
注意各节点bf值得变化
image.png
对parent进行左单旋就是把parent.right提拔成根节点
注意各节点bf值得变化
image.png

补充:相对与根节点各个节点的名称,后续得图会用到这些名称

image.png

image.png
image.png

树parent是pParent(parent.parent)的一棵子树,对parent进行旋转后需要将新的根节点的parent指针指向pParent.

image.png
image.png

//检查 当前是不是就是根节点
if(parent == root) {
    root = subL;
    // subL.parent等于parent,subL提拔成了根节点,所以要将subL.parent设置为null,
    subL.parent = null;
}else {
    //不是根节点,判断这棵子树是左子树还是右子树
    if(pParent.left == parent) {
        pParent.left = subL;
    }else {
        pParent.right = subL;
    }
    subL.parent = pParent;
}

5、左右双旋 &右左双旋

  1. 注意指针指向
  2. 注意维护bf

右左双旋过程图:

image.png
image.png

左右双旋过程图:

image.png
image.png

下面这两幅图介绍了左右双旋时如何维护个节点的bf值(右左双选的不想画了,太累了)

image.png
image.png

/**
 * 左右双旋
 * @param parent
 */
private void rotateLR(TreeNode parent) {
    TreeNode subL = parent.left;
    TreeNode subLR = subL.right;
    int bf = subLR.bf;

    rotateLeft(parent.left);
    rotateRight(parent);

    // 这个规律很重要,中间状态的bf值不重要,根据初始状态的bf值来修改平衡状态的bf值
    if(bf == -1) {
        parent.bf = 1;
        subL.bf = 0;
        subLR.bf = 0;
    }else if(bf == 1){
        parent.bf = 0;
        subL.bf = -1;
        subLR.bf = 0;
    }
}
/**
 * 右左双旋
 * @param parent
 */
private void rotateRL(TreeNode parent) {
    TreeNode subR = parent.right;
    TreeNode subRL = subR.left;
    int bf = subRL.bf;
    
    rotateRight(parent.right);
    rotateLeft(parent);

    // 这个规律很重要,中间状态的bf值不重要,根据初始状态的bf值来修改平衡状态的bf值
    if(bf == 1) {
        parent.bf = -1;
        subR.bf = 0;
        subRL.bf = 0;
    }else if(bf == -1){
        parent.bf = 0;
        subR.bf = 1;
        subRL.bf = 0;
    }
}

6、总代码

package org.example;

/**
 * @Author 12629
 * @Description:
 */
public class AVLTree {
    static class TreeNode {
        public int val;
        public int bf;//平衡因子
        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 parent = null;
        TreeNode cur = root;
        while (cur != null) {
            if(cur.val < val) {
                parent = cur;
                cur = cur.right;
            }else if(cur.val == val) {
                return false;
            }else {
                parent = cur;
                cur = cur.left;
            }
        }
        //cur == null
        if(parent.val < val) {
            parent.right = node;
        }else {
            parent.left = node;
        }
        //
        node.parent = parent;
        cur = node;
        // 平衡因子 的修改
        while (parent != null) {
            //先看cur是parent的左还是右  决定平衡因子是++还是--
            if(cur == parent.right) {
                //如果是右树,那么右树高度增加 平衡因子++
                parent.bf++;
            }else {
                //如果是左树,那么左树高度增加 平衡因子--
                parent.bf--;
            }

            //检查当前的平衡因子 是不是绝对值 1  0  -1
            if(parent.bf == 0) {
                //说明已经平衡了
                break;
            }else if(parent.bf == 1 || parent.bf == -1) {
                //继续向上去修改平衡因子
                cur = parent;
                parent = cur.parent;
            }else {
                //右树高-》需要降低右树的高度
                if(parent.bf == 2) {
                    if(cur.bf == 1) {
                        //左旋
                        rotateLeft(parent);
                    }else {
                        //cur.bf == -1
                        rotateRL(parent);
                    }
                }else {
                    //parent.bf == -2 左树高-》需要降低左树的高度
                    if(cur.bf == -1) {
                        //右旋
                        rotateRight(parent);
                    }else {
                        //cur.bf == 1
                        rotateLR(parent);
                    }
                }
                //上述代码走完就平衡了
                break;
            }
        }
        return true;
    }

    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 = 1;
            subRL.bf = 0;
        }
    }

    /**
     * 左右双旋
     * @param parent
     */
    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) {
            parent.bf = 1;
            subL.bf = 0;
            subLR.bf = 0;
        }else if(bf == 1){
            parent.bf = 0;
            subL.bf = -1;
            subLR.bf = 0;
        }
    }

    /**
     * 左单旋
     * @param parent
     */
    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;
    }

    /**
     * 右单旋
     * @param parent
     */
    private void rotateRight(TreeNode parent) {
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        parent.left = subLR;
        subL.right = parent;
        //没有subLR
        if(subLR != null) {
            subLR.parent = parent;
        }
        //必须先记录
        TreeNode pParent = parent.parent;
        parent.parent = subL;
        //检查 当前是不是就是根节点
        if(parent == root) {
            root = subL;
            // subL.parent等于parent,subL提拔成了根节点,所以要将subL.parent设置为null.
            subL.parent = null;
        }else {
            //不是根节点,判断这棵子树是左子树还是右子树
            if(pParent.left == parent) {
                pParent.left = subL;
            }else {
                pParent.right = subL;
            }
            subL.parent = pParent;
        }
        subL.bf = 0;
        parent.bf = 0;
    }
    //中序遍历的结果是有序的 就能说明当前树 一定是AVL树吗?  不一定的
    private boolean inorder(TreeNode root){
        return inorderHelper(root,Long.MIN_VALUE);
    }
    private boolean inorderHelper(TreeNode root,long pre) {
        if(root == null) {
            return true;
        }
        if(!inorderHelper(root.left,pre)) {
            return false;
        }
        if(pre < root.val){
            pre = root.val;
            if(!inorderHelper(root.right,pre)){
                return false;
            }
            return true;
        }
        return false;
    }

    private int height(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int leftH = height(root.left);
        int rightH = height(root.right);

        return leftH > rightH ? leftH+1 : rightH+1;
    }

    public boolean isBalanced(TreeNode root) {
        if(root == null) {
            return true;
        }
        int leftH = height(root.left);
        int rightH = height(root.right);

        if(rightH-leftH != root.bf) {
            System.out.println("这个节点:"+root.val+" 平衡因子异常");
            return false;
        }

        return Math.abs(leftH-rightH) <= 1
                && isBalanced(root.left)
                && isBalanced(root.right)
                &&inorder(root);// 不仅要左右平衡,还要排序
    }
}

二叉平衡树的适用和不适用场景如下:
适用场景:

  1. 动态数据集合:
    • 当需要频繁地对数据集合进行插入、删除和查找操作时,二叉平衡树是一个很好的选择。它能够保持O(log n)的时间复杂度,避免了普通二叉搜索树在极端情况下退化为链表的问题。
  2. 需要保持数据有序性:
    • 二叉平衡树能够维护数据的有序性,同时也具有较高的查找效率。这在需要保持数据有序性并进行快速查找的场景中非常适用,例如索引数据库、缓存系统等。
  3. 需要高效的范围查询:
    • 由于二叉平衡树能够维护数据的有序性,因此可以很高效地进行范围查询,例如查找某个区间内的所有元素。这在一些需要范围查询的应用中很有用,如地理信息系统、网络路由表管理等。

不适用场景:

  1. 数据集合变化较小:
    • 如果数据集合的变化(插入、删除)很少,使用普通的二叉搜索树可能更加简单高效,因为不需要维护平衡性。
  2. 内存使用要求苛刻:
    • 二叉平衡树需要存储额外的平衡信息(如高度、平衡因子),会占用更多的内存。如果内存使用非常受限,可能需要选择其他更简单的数据结构。
  3. 对写操作要求极高:
    • 由于需要进行平衡操作,二叉平衡树的写操作(插入和删除)会稍微慢于普通的二叉搜索树。如果对写操作的性能要求极高,可能需要考虑其他数据结构。

总的来说,二叉平衡树是一种非常实用的数据结构,在需要高效管理动态有序数据集合的场景中表现优秀。但在某些特定的应用需求中,可能需要根据具体情况来权衡选择适合的数据结构。

** 画图不易,求赞。有不理解的地方可以私信我,必回复! **

<think>好的,我现在需要处理用户关于二叉搜索树左单旋的详细注解及代码实现的请求。首先,我需要回忆一下二叉搜索树(BST)的基本结构以及转操作的作用。转操作主要用于调整树的结构,保持平衡,比如在AVL树或红黑树中应用广泛。用户提到了左单旋,可能是指左这两种基本操作。 接下来,我得确定用户的需求是否具体指AVL树中的转,或者是一般BST的转。根据用户提供的引用,特别是引用[1]中的代码实现和引用[3]中的迭代器代码,可能用户对AVL树的实现比较感兴趣,因为其中涉及到节点的父指针和转调整。但用户的问题是关于左单旋的代码和注解,所以需要明确区分不同树结构中的转操作是否有差异。 然后,我需要回忆左单旋的具体步骤。通常发生在左子树过高的情况下,而左则相反。例如,操作涉及将当前节点的左子节点提升为新的根节点,原根节点成为新根节点的子节点,同时处理子节点的转移。左则是镜像操作,处理子节点的情况。 接下来要考虑如何用代码实现这两个转。根据引用[3]中的代码示例,用户可能已经了解节点结构包含父指针、左子节点等信息。因此,在代码实现中需要处理父指针的更新,左子节点的重新连接,以及可能的子树移动。 然后需要为代码添加详细注解,解释每一步的作用。例如,在时,需要处理原左子节点的子树变为当前节点的左子树,更新各个节点的父指针,确保所有连接正确无误。 另外,用户提供的引用[2]提到伸展树的自调整过程,虽然转的目的不同,但转操作本身可能有相似之处。不过用户的问题集中在二叉搜索树的转,因此重点应放在保持BST性质的前提下调整结构。 还需要考虑是否提供示例图,但用户可能只需要代码和文字解释。根据系统级指令,数学公式需要用$...$,例如节点高度变化可能需要用到变量,但此处可能更多是代码和结构说明。 最后,生成相关问题时要围绕转操作的应用场景、与平衡树的关系、性能影响等方面提问,确保问题相关且有深度。 总结步骤: 1. 定义节点结构,包含父节点、左子节点。 2. 分别实现左函数。 3. 详细注释每个步骤的操作,包括指针调整和父节点更新。 4. 确保代码符合LaTeX格式要求,行内公式正确。 5. 生成相关问题,如转的作用、在AVL树中的应用、时间复杂度等。</think>### 二叉搜索树左单旋详解及代码实现 二叉搜索树的左单旋是平衡树调整的基础操作,主要用于修复局部不平衡结构。以下以****和**左**为例,结合代码实现和详细注解说明。 --- #### **(Right Rotation)** **场景**:当节点$A$的左子树高度过高,需要将左子节点$B$提升为根节点,$A$变为$B$的子节点。 **代码实现(C++风格)**: ```cpp // 操作,参数为转中心节点A void right_rotate(Node* A) { Node* B = A->left; // B是A的左子节点 Node* B_right = B->right; // B的子树 // 1. 更新父节点关系 B->parent = A->parent; if (A->parent) { if (A->parent->left == A) A->parent->left = B; else A->parent->right = B; } // 2. A成为B的子节点 A->parent = B; B->right = A; // 3. B的原子树变为A的左子树 A->left = B_right; if (B_right) B_right->parent = A; // 更新高度(若需要,如AVL树) // update_height(A); // update_height(B); } ``` **注解**: 1. **关系重建**:将$B$的父指针指向$A$的父节点,并更新父节点的子节点指向。 2. **子树迁移**:$B$的原子树变为$A$的左子树,保证BST性质不破坏。 3. **高度更新**:在AVL树中需递归更新节点高度以重新计算平衡因子[^1]。 --- #### **左(Left Rotation)** **场景**:当节点$A$的子树高度过高,需将子节点$B$提升为根节点,$A$变为$B$的左子节点。 **代码实现**: ```cpp void left_rotate(Node* A) { Node* B = A->right; // B是A的子节点 Node* B_left = B->left; // B的左子树 // 1. 更新父节点关系 B->parent = A->parent; if (A->parent) { if (A->parent->left == A) A->parent->left = B; else A->parent->right = B; } // 2. A成为B的左子节点 A->parent = B; B->left = A; // 3. B的原左子树变为A的子树 A->right = B_left; if (B_left) B_left->parent = A; // 更新高度(若需要) // update_height(A); // update_height(B); } ``` **注解**: 1. **对称性**:左的镜像操作,步骤完全对称。 2. **平衡恢复**:通过转降低子树高度差,例如在AVL树中使平衡因子绝对值不超过1[^1]。 --- #### **关键点总结** 1. **指针更新顺序**:必须按照父节点→子节点→子树的顺序调整,避免指针丢失。 2. **时间复杂度**:单次转时间复杂度为$O(1)$,但需在平衡树中结合回溯调整[^2]。 3. **应用场景**:AVL树插入/删除后的再平衡、伸展树的节点提升[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值