• 平衡二叉树AVL


      对于二叉查找树,当插入节点时,在一些情况下,而二叉树会出现不平衡的状况,即将节点都插入到了二叉树的左子树上;使得二叉树的查找性能大打折扣,因此,为了解决了二叉查找树退化成链表的问题,引入了平衡二叉树。

    平衡二叉树(AVL)

    定义:

      平衡二叉树

    1.左、右子树的高度差的绝对值小于等于1;

    2.左、右子树也分别为平衡二叉树。

      平衡因子-将二叉树上节点的左子树减去右子树高度的值,称为该节点的平衡因子BF。

      最小不平衡子树-距离插入节点最近的,且以平衡因子的绝对值大于1的节点为根的子树。

    typedef int TreeKeyType;
    
    struct AvlNode
    {
         TreeKeyType data;
         int height;               //结点所在高度
         AvlNode *left;
         AvlNode> *right;
    
         //构造函数
         AvlNode(Data) : data(Data), left(NULL), right(NULL), height(0){}
     };        

    插入(可能导致失衡,须进行调整):

     void Insert(AvlNode *&t, TreeKeyType x)
     {
         if (t == NULL)
             t = new AvlNode(x);
    
      //插入到左子树
         else if (x < t->data)
         {
             Insert(t->left, x);
    
             //判断平衡情况,若左子树比右子树高导致不平衡
             if (GetHeight(t->left) - GetHeight(t->right) > 1)
             {
                 if (x < t->left->data)
                     //向左子树插入左孩子导致失衡
                     RR(t);//右单旋转
                 else
                     //向左子树插入右孩子导致失衡
                     LR(t);//先左后右旋转
             }
         }
    
    
         //插入到右子树
         else if (x > t->data)
         {
             Insert(t->right, x);
    
             if (GetHeight(t->right) - GetHeight(t->left) > 1)
             {
                 if (x > t->right->data)
                     LL(t);
                 elseRL(t);
             }
         }
    
    
         //数据重复,不进行插入  
         else
             return;
    
         //更新树的高度
         t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
     }

    1、左单旋转
      向右子树插入右孩子导致AVL失衡时,需要围绕最小失衡子树的根节点进行左单旋转。

    void RR(AvlNode *& t)
     {
         AvlNode *q = t->right;//q指向t的右子树的根节点
         t->right = q->left;//q的左子树挂为t的右子树
         q->left = t;//t变为q的左子树
         t = q;
    
         t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
     }

    2、右单旋转

      向左子树插入左孩子导致AVL失衡时,需要围绕最小失衡子树的根节点进行右单旋转。

    void LL(AvlNode *& t)
     {
         AvlNode *q = t->left;
         t->left = q->right;
         q->right = t;
         t = q;
    
         t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
     }

    3、先左旋后右旋
      在左子树上插入右孩子导致AVL树失衡时,需要进行先左旋后右旋。

     LR(AvlNode *& t)
     {
         //双旋转可以通过两次单旋转实现
         //对t的左结点进行RR旋转,再对根节点进行LL旋转
         RR(t->left);
         LL(t);
     }

    4、先右旋后左旋
      在右子树上插入左孩子导致AVL树失衡时,需要进行先右旋后左旋。

    void RL(AvlNode *& t)
     {
         LL(t->right);
         RR(t);
     }

    删除

    1、删除右子树的节点
      删除右子树的节点导致AVL树失衡,相当于在左子树插入新节点导致AVL树失衡,应进行右单旋转或先右旋后左旋。
    2、删除左子树的节点
      删除左子树的节点导致AVL树失衡,相当于在右子树插入新节点导致AVL树失衡,应进行左单旋转或者先左旋后右旋。

    AvlNode * FindMax(AvlNode *t) const
    {
        if (t == NULL)
            return NULL;
        if (t->right == NULL)
            return t;
        return FindMax(t->right);
    }
    
    
    AvlNode * FindMin(AvlNode *t) const
    {
        if (t == NULL)
            return NULL;
        if (t->left == NULL)
            return t;
        return FindMin(t->left);
    }
    
    
    int GetHeight(AvlNode *t)
    {
        if (t == NULL)
            return -1;
        else
            return t->height;
    }
    
    
    bool Delete(AvlNode *&t, T x)
    {
        //t为空 未找到要删除的结点
        if (t == NULL)
            return false;
        //找到了要删除的结点
        else if (t->data == x)
        {
            //左右子树都非空
            if (t->left != NULL && t->right != NULL)
            {//在高度更大的那个子树上进行删除操作
    
                //左子树高度大,删除左子树中值最大的结点,将其赋给根结点
                if (GetHeight(t->left) > GetHeight(t->right))
                {
                    t->data = FindMax(t->left)->data;
                    Delete(t->left, t->data);
                }
                else//右子树高度更大,删除右子树中值最小的结点,将其赋给根结点
                {
                    t->data = FindMin(t->right)->data;
                    Delete(t->right, t->data);
                }
            }
            else
            {//左右子树有一个不为空,直接用需要删除的结点的子结点替换即可
                AvlNode *old = t;
                t = t->left ? t->left: t->right;//t赋值为不空的子结点
                delete old;
            }
        }
        else if (x < t->data)//要删除的结点在左子树上
        {
            //递归删除左子树上的结点
            Delete(t->left, x);
            //判断是否仍然满足平衡条件
            if (GetHeight(t->right) - GetHeight(t->left) > 1)
            {
                if (GetHeight(t->right->left) > GetHeight(t->right->right))
                {
                    //RL双旋转
                    t = RL(t);
                }
                else
                {//RR单旋转
                    t = RR(t);
                }
            }
            else//满足平衡条件 调整高度信息
            {
                t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
            }
        }
        else//要删除的结点在右子树上
        {
            //递归删除右子树结点
            Delete(t->right, x);
            //判断平衡情况
            if (GetHeight(t->left) - GetHeight(t->right) > 1)
            {
                if (GetHeight(t->left->right) > GetHeight(t->left->left))
                {
                    //LR双旋转
                    t = LR(t);
                }
                else
                {
                    //LL单旋转
                    t = LL(t);
                }
            }
            else//满足平衡性 调整高度
            {
                t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
            }
        }
    
        return true;
    }

    查找和遍历同二叉查找树

    原文链接 https://www.cnblogs.com/zhangbaochong/p/5164994.html

  • 相关阅读:
    基础操作
    需要注意
    简单操作
    git指令-版本回退
    设计模式-代理模式
    在idea下遇到的问题汇总
    maven笔记--持续更新
    poi简介
    Win10添加右键在此处打开命令行
    Ajax&Json案例
  • 原文地址:https://www.cnblogs.com/yongjin-hou/p/14534221.html
Copyright © 2020-2023  润新知