【数据结构】7.平衡搜索树(AVL树和红黑树)

这篇具有很好参考价值的文章主要介绍了【数据结构】7.平衡搜索树(AVL树和红黑树)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

0. 概述

  对于普通的搜索树,如果一直插入比第一个元素小的元素,它会退化成一个无限向左下角眼神的单链表,使得时间复杂度退化为O(n)。如果我们在插入时保持树的结构是平衡的,则可以保证查找、插入和删除的时间复杂度有对数级的时间性能,下面讲到的AVL树和红黑树都是平衡搜索树,通过旋转来保持平衡

【数据结构】7.平衡搜索树(AVL树和红黑树)

1. AVL树

1.1 定义

  Adelson-Velskii 和 Landis 在 1962年提出的一种平衡树,保证搜索树的高度是O(logn),这样就可以保证查找、插入和删除的时间为O(logn)

1.2 AVL树的描述

  AVL 树一般用链表描述,为了简化插入和删除操作,为每个节点增加一个平衡因子 bf ,平衡因子 bf(x) 的定义为:x 的左子树的高度 - x 的右子树的高度

  从 AVL 树的定义可以知道,平衡因子 bf 的取值为 -1、0 和 1

【数据结构】7.平衡搜索树(AVL树和红黑树)

1.3 AVL树的搜索

  与普通的搜索树相同,根据 theKey 不断向左孩子或右孩子移动寻找即可,时间复杂度为O(logn)

1.4 AVL树的插入

  首先是区分4种旋转情况的代码,具体在1.4.1-1.4.4部分

template<class K, class V>
bool avlTree<K, V>::insert(K key, V val)
{
    // 1.根节点为空,直接插入
    if (_root == NULL)
    {
        _root = new Node<K, V>(key, val);
        return true;
    }
    // 2.根节点不为空
    else
    {
        Node<K, V>* cur = _root;
        Node<K, V>* parent = NULL;

        // 2.1)找到要插入节点的位置
        while (cur!=NULL)
        {
            parent = cur;
            if (cur->_key > key)
                cur = cur->_left;
            else if (cur->_key < key)
                cur = cur->_right;
            else
                return false;   //不允许出现重复元素的节点
        }

        // 2.2)插入新节点
        cur = new  Node<K, V>(key, val);
        if (parent->_key > key)
        {
            parent->_left = cur;
            cur->_parent = parent;
        }
        else
        {
            parent->_right = cur;
            cur->_parent = parent;
        }

        // 2.3)插入完成后,调整平衡因子
        while (parent!=NULL)
        {
            if (cur == parent->_left)//插入节点在左子树父节点bf++,反之--
                parent->_bf++;
            else
                parent->_bf--;

            // 2.3.1)插入新节点后,双亲结点高度为0, 说明这个父节点原先已有一个孩子, 这次插入到另一个孩子的位置了, 树整体的高度无变化, 结束
            if (parent->_bf == 0)
                break;
            // 2.3.2)插入节点后双亲节点高度为-1或1, 说明子树高度改变,则继续向上调整
            else if (parent->_bf == -1 || parent->_bf == 1)
            {
                cur = parent;
                parent = parent->_parent;
            }
            // 2.3.3)插入节点后parent->_bf==-2||parent->_bf==2;说明已经不平衡,需要旋转
            else
            {
                if (parent->_bf == 2)
                {
                    if (cur->_bf == 1)
                        rotateLL(parent);    // parent(2), child(1)
                    else
                        rotateLR(parent);    // parent(2), child(-1)
                    
                }
                else
                {
                    if (cur->_bf == -1)
                        rotateRR(parent);    // parent(-2), child(-1)
                    else
                        rotateRL(parent);    // parent(-2), child(1)
                }
                break;
            }
        }
        return true;
    }
}

1.4.1 LL型不平衡(单旋转)

  插入前左子树高度比右子树高度高 1,然后在左子树的左侧插入一个新的元素,只需要一次 右单旋 就可以转为平衡搜索树。具体操作如下,根节点A的左孩子B转换为新的根节点,B的右孩子转换为A的左孩子

【数据结构】7.平衡搜索树(AVL树和红黑树)

template <class K, class V>
void avlTree<K, V>::rotateLL(Node<K, V>* parent)
{
    Node<K, V>* subL = parent->_left;
    Node<K, V>* subLR = subL->_right;
    Node<K, V>* ppNode = parent->_parent;

    // 一共两步, 重新连接节点即可
    parent->_left = subLR;      // 1.当前 parent 节点的左孩子 改成 其左孩子的右孩子
    if (subLR != NULL)
        subLR->_parent = parent;

    subL->_right = parent;      // 2.把当前 parent 节点改成 subL 的右孩子
    parent->_parent = subL;

    if (_root == parent)        // 判断不平衡的点是不是根节点
    {
        _root = subL;
        subL->_parent = NULL;
    }
    else
    {
        if (ppNode->_right == parent)
        {
            ppNode->_right = subL;
        }
        else
        {
            ppNode->_left = subL;
        }

        subL->_parent = ppNode;
    }

    subL->_bf = 0;
    parent->_bf = 0;
}

1.4.2 RR型不平衡(单旋转)

  插入前右子树高度比左子树高度高 1,然后在右子树的右侧插入一个新的元素,只需要一次 左单旋 就可以转为平衡搜索树。具体操作如下,根节点A的右孩子B转换为新的根节点,B的左孩子转换为A的右孩子

【数据结构】7.平衡搜索树(AVL树和红黑树)

template <class K, class V>
void avlTree<K, V>::rotateRR(Node<K, V>* parent)
{
    Node<K, V>* subR = parent->_right;
    Node<K, V>* subRL = subR->_left;
    Node<K, V>* pParent = parent->_parent;

    // 一共两步, 重新连接节点即可
    parent->_right = subRL;      // 1.当前 parent 节点的右孩子 改成 其右孩子的左孩子
    if (subRL != NULL)
        subRL->_parent = parent;

    subR->_left = parent;       // 2.把当前 parent 节点改成 subR 的左孩子
    parent->_parent = subR;

    if (parent == _root)        // 判断不平衡的点是不是根节点
    {
        _root = subR;
        _root->_parent = NULL;
    }

    else
    {
        if (pParent->_left = parent)
            pParent->_left = subR;
        else
            pParent->_right = subR;

        subR->_parent = pParent;
    }
    parent->_bf = 0;
    subR->_bf = 0;
}

1.4.3 LR型不平衡(双旋转)

  左子树高度更高的情况下,在左子树的右侧插入一个节点。首先进行一次 左单旋 ,将它转换为LL型不平衡,然后进行一次 右单旋 转换为平衡搜索树

【数据结构】7.平衡搜索树(AVL树和红黑树)

template <class K, class V>
void avlTree<K, V>::rotateLR(Node<K, V>* parent)
{
    Node<K, V>* subL = parent->_left;
    Node<K, V>* subLR = subL->_right;
    int bf = subLR->_bf;

    rotateRR(parent->_left);
    rotateLL(parent);

    if (bf == 1)
    {
        parent->_bf = 0;
        subL->_bf = -1;
        subLR->_bf = 0;
    }
    else if (bf == -1)
    {
        parent->_bf = 1;
        subL->_bf = 0;
        subLR->_bf = 0;
    }
}

1.4.4 RL型不平衡(双旋转)

   与LR型不平衡类似,这里直接给出代码

template <class K, class V>
void avlTree<K, V>::rotateRL(Node<K, V>* parent)
{
    Node<K, V>* subR = parent->_right;
    Node<K, V>* subRL = subR->_left;
    int bf = subRL->_bf;

    rotateLL(parent->_right);
    rotateRR(parent);

    if (bf == 1)
    {
        subR->_bf = 0;
        parent->_bf = -1;
        subRL->_bf = 0;
    }
    else if (bf == -1)
    {
        parent->_bf = 0;
        subR->_bf = 1;
        subRL->_bf = 0;
    }
}

2. 红黑树(red-black tree)

2.1 基本概念

  红黑树是一棵扩充二叉树,每个空指针用一个外部节点来代替,除此之外还有以下性质

  • 根节点和外部节点颜色都是黑色
  • 在根节点到外部节点的路径上,没有连续两个节点是红色
  • 在所有根节点到外部节点的路径上,黑色节点的数目都相同

  红黑树一个节点的阶(rank):从该节点到一外部节点的路径上黑色节点的数量

  红黑树最大的高度是2log2(n+1)

2.2 RBT的搜索

  与普通的搜索树相同,根据 theKey 不断向左孩子或右孩子移动寻找即可,时间复杂度为O(logn)

2.3 RBT的插入

  我们的插入目标实际上是,和普通搜索树一样插入一个元素,然后再让它额外满足红黑树的性质。

2.3.1 情况一:变色处理

  这种情况是最简单的情况,如果插入节点的叔叔节点(父亲的对称孩子)也是红色,则只需要进行变色处理

  • 父亲节点和叔叔节点变为黑色
  • 曾祖父节点变为红色

  循环处理直到根节点为止,最后将根节点变为黑色结束

【数据结构】7.平衡搜索树(AVL树和红黑树)

2.3.2 情况二:单旋加变色处理

   如果新插入节点的叔叔为黑色,并且新插入节点在外侧

  • 进行一次单旋转
  • 把父亲节点更改为黑色,曾祖父节点更改为红色(最后这个三角形是黑色连两个红色)

【数据结构】7.平衡搜索树(AVL树和红黑树)

2.3.3 情况三:双旋加变色处理

   如果新插入节点的叔叔为黑色,并且新插入节点在内测

  • 对父亲节点进行一次单旋转
  • 对曾祖父节点进行一次单旋转
  • 将新插入节点修改为黑色,曾祖父节点修改为红色(最后这个三角形是黑色连两个红色)

【数据结构】7.平衡搜索树(AVL树和红黑树)

2.3.4 RBT插入的实现

2.3.4.1 对外暴露的插入函数

template <class K, class V>
bool RBTree<K, V>::insert(K key, V val)
{
    RBTNode<K, V>* z = NULL;

    if ((z = new RBTNode<K, V>(key, val, RED, NULL, NULL, NULL)) == NULL)
        return false;

    return insert(this->_root, z);
}

2.3.4.2 按照普通的搜索树进行插入操作

template <class K, class V>
bool RBTree<K, V>::insert(RBTNode<K, V>*& root, RBTNode<K, V>* node)
{
    // 1.根节点为空,直接插入
    if (root == NULL)
    {
        node->_color = BLACK;
        root = node;
        return true;
    }
    // 2.根节点不为空
    else
    {
        RBTNode<K, V>* cur = root;
        RBTNode<K, V>* parent = NULL;

        // 2.1)找到要插入节点的位置
        while (cur != NULL)
        {
            parent = cur;
            if (node->_key < cur->_key)
                cur = cur->_left;
            else if (node->_key > cur->_key)
                cur = cur->_right;
            else
                return false;   //不允许出现重复元素的节点
        }

        // 2.2)插入新节点
        if (parent->_key > node->_key)
        {
            parent->_left = node;
            node->_parent = parent;
        }
        else
        {
            parent->_right = node;
            node->_parent = parent;
        }

        return insertFixUp(root, node);
    }
}

2.3.4.3 插入完成后的颜色修正

template <class K, class V>
bool RBTree<K, V>::insertFixUp(RBTNode<K, V>*& root, RBTNode<K, V>* node)
{
    RBTNode<K, V>* parent, * grandparent, * cur;
    cur = node;
    parent = cur->_parent;

    // 若“父节点存在,并且父节点的颜色是红色”
    while (parent && rb_is_red(parent))
    {
        grandparent = parent->_parent;

        //若“父节点”是“祖父节点的左孩子”
        if (parent == grandparent->_left)
        {
            RBTNode<K, V>* uncle = grandparent->_right;

            if (uncle && rb_is_red(uncle))
            {// 情况1:叔叔节点是红色, 修改后继续检查
                rb_set_black(uncle);
                rb_set_black(parent);
                rb_set_red(grandparent);
                cur = grandparent;
                parent = cur->_parent;
            }
            else
            {// 情况2: 叔叔节点不存在或者是黑色, 修改后结束循环
                if (parent->_left == cur)
                {// 情况2.1:叔叔是黑色,且当前节点是左孩子 (单旋+变色)
                    rightRotate(grandparent);
                    rb_set_black(parent);
                    rb_set_red(grandparent); 
                }
                else
                {// 情况2.2:叔叔是黑色,且当前节点是右孩子
                    leftRotate(parent);
                    rightRotate(grandparent);
                    rb_set_black(cur);
                    rb_set_red(grandparent);
                }
                break;
            }
        }
        else//若“父节点”是“祖父节点的右孩子”
        {
            RBTNode<K, V>* uncle = grandparent->_left;

            if (uncle && rb_is_red(uncle))
            {
                rb_set_black(uncle);
                rb_set_black(parent);
                rb_set_red(grandparent);
                cur = grandparent;
                parent = cur->_parent;
            }
            else
            {
                if (parent->_right == cur)
                {
                    leftRotate(grandparent);
                    rb_set_black(parent);
                    rb_set_red(grandparent);
                }
                else
                {
                    rightRotate(parent);
                    leftRotate(grandparent);
                    rb_set_black(cur);
                    rb_set_red(grandparent);
                }
            }
        }
    }
    // 将根节点设为黑色
    rb_set_black(root);
    return true;
}

 文章来源地址https://www.toymoban.com/news/detail-711175.html

到了这里,关于【数据结构】7.平衡搜索树(AVL树和红黑树)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构】平衡二叉搜索树(AVL树)——AVL树的概念和介绍、AVL树的简单实现、AVL树的增删查改

      为什么要引入平衡二叉搜索树?   在之前我们学习了二叉搜索树,二叉搜索树的结构类似于一个倒置的树,而左子树的值小于根节点的值,右节点的值大于根节点的值,这种结构使得二叉搜索树在处理有序数据时非常高效。但是如果 在传入的数据为有序或接近有序,

    2024年02月07日
    浏览(37)
  • (浙大陈越版)数据结构 第三章 树(中) 二叉搜索树和平衡二叉树

    目录 4.1.1 二叉搜索树及查找 什么是二叉搜索树 定义 二叉搜索树特殊函数集: 查找操作:Find 算法思想 代码实现 补:查找最大和最小元素 4.1.2 二叉搜索树的插入 插入操作:Insert 算法思想 代码实现 例题 4.1.3 二叉搜索树的删除 删除操作:delete 算法思想 情况1:删除叶节点

    2024年02月08日
    浏览(39)
  • C++数据结构之平衡二叉搜索树(一)——AVL的实现(zig与zag/左右双旋/3+4重构)

    本文是介绍众多平衡二叉搜索树(BBST)的第一篇——介绍AVL树。故先来引入BBST的概念。由于上一篇介绍的二叉搜索树(BST)在极度退化的情况下,十分不平衡,不平衡到只朝一侧偏,成为一条链表,复杂度可达 O ( n ) O(n) O ( n ) ,所以我们要在“平衡”方面做一些约束,以防

    2024年02月13日
    浏览(25)
  • 数据结构——常见二叉树的分类(完全二叉树、满二叉树、平衡二叉树、二叉搜索树、红黑树)

    专业术语 中文 描述 Root 根节点 一棵树的顶点 Child 孩子结点 一个结点含有的子树的根节点称为该结点的子节点 Leaf 叶子结点 没有孩子的节点 Degree 度 一个节点包含子树的数量 Edge 边 一个节点与另外一个节点的连接 Depth 深度 根节点到这个节点经过边的数量 Height 节点高度 从

    2024年02月03日
    浏览(35)
  • 【数据结构】AVL树/红黑树

    目录 1.AVL树(高度平衡二叉搜索树) 10.1.基本概念 10.2.实现 10.2.1.AVL树节点的定义 10.2.2.AVL树的插入 10.2.3.AVL树的旋转 1.新节点插入较高左子树的左侧---左左:右单旋 2.新节点插入较高右子树的右侧---右右:左单旋 3.新节点插入较高左子树的右侧---左右:先左单旋再右单旋(左

    2024年02月15日
    浏览(34)
  • Python - 深夜数据结构与算法之 AVL 树 & 红黑树

    目录 一.引言 二.高级树的简介 1.树 2.二叉树 3.二叉搜索树 4.平衡二叉树 三.AVL 树 ◆ 插入节点 ◆ 左旋 ◆ 右旋 ◆ 左右旋 ◆ 右左旋 ◆ 一般形式 ◆ 实际操作 ◆ 总结 四.红黑树 ◆ 概念 ◆ 示例 ◆ 对比 五.总结 前面我们介绍了二叉树、二叉搜索树、多叉树等基础的树形结构,

    2024年01月19日
    浏览(32)
  • 【数据结构与算法】平衡二叉树(AVL树)

    给你一个数列{1,2,3,4,5,6},要求创建二叉排序树(BST),并分析问题所在。 BST 存在的问题分析 : 左子树全部为空,从形式上看,更像一个单链表。 插入速度没有影响。 查询速度明显降低(因为需要依次比较),不能发挥 BST 的优势,因为每次还需要比较左子树,其查询速度

    2024年02月13日
    浏览(34)
  • 数据结构之二叉树和平衡二叉树

    1、二叉树: 2、平衡二叉树:

    2024年04月17日
    浏览(34)
  • 数据结构07:查找[C++][平衡二叉排序树AVL]

    图源:文心一言 考研笔记整理1w+字,小白友好、代码可跑,请小伙伴放心食用~~🥝🥝 第1版:查资料、写BUG、画导图、画配图~🧩🧩 参考用书: 王道考研《2024年 数据结构考研复习指导》 参考用书配套视频: 7.3_2 平衡二叉树_哔哩哔哩_bilibili 特别感谢:  Chat GPT老师、文心

    2024年02月11日
    浏览(38)
  • 数据结构奇妙旅程之二叉平衡树进阶---AVL树

    ꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好,我是xiaoxie.希望你看完之后,有不足之处请多多谅解,让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转载还请通知˶⍤⃝˶ 个人主页:xiaoxieʕ̯

    2024年03月13日
    浏览(76)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包