首页 > 其他分享 >30. 平衡二叉树

30. 平衡二叉树

时间:2023-06-07 12:44:58浏览次数:46  
标签:Positon Right 30 Height K1 二叉树 平衡 节点 Left

一、什么是平衡二叉树

  平衡二叉树(Balance Factor Tree,简称 AVL树)是一个特殊的二叉树,它可以是空树。如果不为空,它任一节点左、右子树高度差的绝对值不超过 1,即 |BF(T)| ≤ 1。其中,BF 是指 平衡因子(Balance Factory):\(BF(T) = h_{L} - h_{R}\),其中 \(h_{L}\) 和 \(h_{R}\) 分别为 T 的左、右子树的高度。

二叉搜索树节点不同插入次序,将导致不同的深度和平均查找长度 ASL。

给定节点数为 n 的 AVL树 的最大高度为 \(log_{2}N\)

平衡二叉树最大深度为 \(O(log_{2}N)\),平均查找长度/查找的时间复杂度为 \(O(log_{2}N)\)

二、AVL树的表示

typedef int ElementType;
typedef struct AvlNode *AvlTree;
typedef struct AvlNode *Positon;

struct AvlNode
{
    ElementType Element;        // AVL树的元素
    AvlTree Left;               // AVL树的左儿子节点
    AvlTree Right;              // AVL树的右儿子节点
    int Height;                 // AVL树的高度
};

三、获取AVL树的高度

/**
 * @brief 获取指定节点在AVL树中的高度
 * 
 * @param P 待查询的节点
 * @return int 返回指定节点在AVL树中的高度
 */
int Height(Positon P)
{
    if(P == NULL)
    {
        return -1;
    }
    else
    {
        return P->Height;
    }
}

四、平衡二叉树的调整

  从插入点往回找到第一个不平衡节点,调整以该节点为根的子树。每次调整的对象都是“最小不平衡子树”。

4.1、平衡二叉树LL旋转

img

  由于在节点 A 的左孩子节点 B 的左子树 \(B_{L}\) 上插入了新节点,A 的平衡因子由 1 增至 2,导致以 A 为根的子树失去平衡,需要一次向右的旋转操作。

  为了使树平衡,我们把 \(B_{L}\) 和 C 上移一层,并把 A 和 \(A_{R}\) 下移一层。这时,我们需要把 B 作为新的树根。在原二叉树中,B < A,于是在新树中,A 变成了 B 的右儿子,\(B_{L}\) 和 \(A_{R}\) 仍然分别是 B 的左儿子和 A 的右儿子。子树 \(B_{R}\) 包含原树中介于 A 和 B 之间的那些节点,我们可以将它放在新树中 A 左儿子位置上。

/**
 * @brief LL左旋
 * 
 * @param K2 原树的树根
 * @return Positon 新树的树根
 */
Positon SingleRotateWithLeft(Positon K2)
{
    Positon K1;

    K1 = K2->Left;
    K2->Left = K1->Right;
    K1->Right = K2;

    K2->Height = Max(Height(K2->Left),Height(K2->Right)) + 1;
    K1->Height = Max(Height(K1->Left),K2->Height ) + 1;

    return K1;
}

4.2、平衡二叉树RR旋转

img

  由于在节点 A 的右孩子节点 B 的右子树 \(B_{R}\) 上插入了新节点,A 的平衡因子由 1 增至 2,导致以 A 为根的子树失去平衡,需要一次向左的旋转操作。

  为了使树平衡,我们把 \(B_{R}\) 和 C 上移一层,并把 A 和 \(A_{L}\) 下移一层。这时,我们需要把 B 作为新的树根。在原二叉树中,B > A,于是在新树中,A 变成了 B 的左儿子,\(A_{L}\) 和 \(B_{R}\) 仍然分别是 A 的左儿子和 B 的右儿子。子树 \(B_{L}\) 包含原树中介于 A 和 B 之间的那些节点,我们可以将它放在新树中 A 的右儿子位置上。

/**
 * @brief RR旋转
 * 
 * @param K1 原树的树根
 * @return Positon 新树的树根
 */
Positon SingleRotateWithRight(Positon K1)
{
    Positon K2;

    K2 = K1->Right;
    K1->Right = K2->Left;
    K2->Left = K1;

    K1->Height = Max(Height(K1->Left),Height(K1->Right)) + 1;
    K2->Height = Max(K1->Height,Height(K2->Right)) + 1;

    return K2;
}

4.3、平衡二叉树的LR旋转

img

img

  为了使树平衡,我们把 C 作为根节点,这迫使 B 成为 C 的左儿子,A 成为 C 的右儿子,\(B_{L}\) 和 \(A_{R}\) 仍然分别是 B 的左儿子和 A 的右儿子。子树 \(C_{L}\) 包含原树中小于 C 但是比 B 大的那些节点。因此,我们可以将它放在新树中 B 右儿子位置上。子树 \(C_{R}\) 包含原树中大于 C 但是比 A 小的那些节点。因此,我们可以将它放在新树中 A 左儿子位置上。

/**
 * @brief LR旋转
 * 
 * @param K3 原树的树根
 * @return Positon 新树的树根
 */
Positon DoubleRotateWithLeft(Positon K3)
{
    K3->Left = SingleRotateWithRight(K3->Left);
    return SingleRotateWithLeft(K3);
}

4.4、平衡二叉树的RL旋转

img

img

  为了使树平衡,我们把 C 作为根节点,这迫使 A 成为 C 的左儿子,B 成为 C 的右儿子,\(A_{L}\) 和 \(B_{R}\) 仍然分别是 A 的左儿子和 B 的右儿子。子树 \(C_{L}\) 包含原树中小于 C 但是比 A 大的那些节点。因此,我们可以将它放在新树中 A 右儿子位置上。子树 \(C_{R}\) 包含原树中大于 C 但是比 B 小的那些节点。因此,我们可以将它放在新树中 B 左儿子位置上。

/**
 * @brief RL旋转
 * 
 * @param K1 原树的树根
 * @return Positon 新树的树根
 */
Positon DoubleRotateWithRight(Positon K1)
{
    K1->Right = SingleRotateWithRight(K1->Right);
    return SingleRotateWithRight(K1);
}

五、向AVL树插入节点

/**
 * @brief 向AVL树中插入节点
 * 
 * @param X 待插入的元素
 * @param T 待操作的AVL树
 * @return AvlTree 返回指向AVL树根节点的指针
 */
AvlTree Insert(ElementType X, AvlTree T)
{
    if(T == NULL)                                           // 如果树为空,则创建根节点
    {
        T = (AvlTree)malloc(sizeof(struct AvlNode));
        if(T == NULL)
        {
            printf("创建根节点失败!\n");
        }
        else
        {
            T->Element = X;
            T->Height = 0;
            T->Left = T->Right = NULL;
        }
    }
    else
    {
        if(X < T->Element)                                  // 如果X比当前节点小,则递归插入当前节点的左子树
        {
            T->Left = Insert(X, T->Left);
            if(Height(T->Left) - Height(T->Right) == 2)     // 新插入的节点使AVL树不平衡了
            {
                if(X < T->Left->Element)                    // 如果新插入的节点在被破化者的左子树的左子树上
                {
                    T = SingleRotateWithLeft(T);            // LL旋转
                }
                else                                        // 如果新插入的节点在被破化者的左子树的右子树上
                {
                    T = DoubleRotateWithLeft(T);            // LR旋转
                }
            }
        }
        else if(X > T->Element)                             // 如果X比当前节点大,则递归插入当前节点的右子树
        {
            T->Right = Insert(X,T->Right);
            if(Height(T->Right) - Height(T->Left) == 2)     // 新插入的节点使AVL树不平衡了
            {
                if(X > T->Right->Element)                   // 如果新插入的节点在被破化者的右子树的右子树上
                {
                    T = SingleRotateWithRight(T);           // RR旋转
                }
                else                                        // 如果新插入的节点在被破化者的右子树的左子树上
                {
                    T = DoubleRotateWithRight(T);           // RL旋转
                }
            }
        }
        // 如果相等的话,这里不做处理
    }

    T->Height = Max(Height(T->Left),Height(T->Right));      // 更新树的高度
  
    return T;
}

标签:Positon,Right,30,Height,K1,二叉树,平衡,节点,Left
From: https://www.cnblogs.com/kurome/p/17463007.html

相关文章

  • CMT2300A 433MHz SUB-1G无线收发芯片
    CMT2300A是一款超低功耗,高性能,适用于各种140至1020MHz无线应用的OOK,(G)FSK射频收发器。它是CMOSTEKNextGenRFTM射频产品线的一部分,这条产品线包含完整的发射器,接收器和收发器。CMT2300A的高集成度,简化了系统设计中所需的外围物料。高达+20dBm及-121dBm的灵敏度优化了......
  • ABB CI854AK01-EA 3BSE030220R2
    W;① ⑧ 0 ③ 0 ① 7 7 7 ⑤ 9ABB CI854AK01-EA3BSE030220R2  PFCL201CE-50KN   5SHY5045L0020  PM891 3BSE053240R1  5SHY4045L00013BHB018162R0001  PFSK152  PM891K013BSE053241R1  3BHB020720R0002  一个微调的单轴系统和一个节......
  • SSO2.0 14-20230607
                  ......
  • #yyds干货盘点# LeetCode程序员面试金典:二叉树的右视图
    1.简述:给定一个二叉树的根节点root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。 示例1:输入: [1,2,3,null,5,null,4]输出: [1,3,4]示例2:输入: [1,null,3]输出: [1,3]示例3:输入: []输出: []2.代码实现:classSolution{publicList<I......
  • 雄迈300万低功耗无线摄像机拆机测试分析
    今日收到一台雄迈300万低功耗无线摄像机,对它进行拆机测试分析,看是否有什么值得学习的地方。(一)设备外观结构外观:一个太阳能充电板两个wifi天线一个摄像头一个PIR透镜(二)芯片组成主处理器:50V200SD1148_224609flash型号:FM25Q128Awifi芯片:海思Hi3861L充电芯片:SG......
  • #yyds干货盘点# LeetCode程序员面试金典:二叉树中的最大路径和
    题目:二叉树中的路径被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中至多出现一次。该路径至少包含一个节点,且不一定经过根节点。路径和是路径中各节点值的总和。给你一个二叉树的根节点root,返回其最大路径和。 示例1:输入:root=......
  • 2023-06-06:给你二叉树的根结点 root ,请你设计算法计算二叉树的 垂序遍历 序列。 对位
    2023-06-06:给你二叉树的根结点root,请你设计算法计算二叉树的垂序遍历序列。对位于(row,col)的每个结点而言,其左右子结点分别位于(row+1,col-1)和(row+1,col+1)树的根结点位于(0,0)。二叉树的垂序遍历从最左边的列开始直到最右边的列结束,按列索引每一列上......
  • CS5290兼容CS5230防破音AB/D切换,5.2W单声道GF类音频功放IC
    CS5290E是一款采用CMOS工艺,电容式升压型GF类单声道音频功放,可以为4Q的负载提供最高5.2W的连续功率;CS5290E芯片内部固定的28倍增益,有效的减少了外围元器件的数量;功放集成了D类和AB类两种工作模式即可保证D类模式下强劲的功率输出,又可兼顾系统在有FM的情况下,消除功放对系统的干扰;CS52......
  • 230606蓝桥训练
    重现A-数数#include<bits/stdc++.h>usingnamespacestd;intmain(){strings;set<char>cnt;cin>>s;for(autoc:s)cnt.insert(c);cout<<cnt.size()<<"\n";return0;}B-二进制?十......
  • 2023-06-06:给你二叉树的根结点 root ,请你设计算法计算二叉树的 垂序遍历 序列。 对位
    2023-06-06:给你二叉树的根结点root,请你设计算法计算二叉树的垂序遍历序列。对位于(row,col)的每个结点而言,其左右子结点分别位于(row+1,col-1)和(row+1,col+1)树的根结点位于(0,0)。二叉树的垂序遍历从最左边的列开始直到最右边的列结束,按列索引每一......