我的编程空间,编程开发者的网络收藏夹
学习永远不晚

C++如何实现AVL树

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

C++如何实现AVL树

本篇内容介绍了“C++如何实现AVL树”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

AVL 树的概念

也许因为插入的值不够随机,也许因为经过某些插入或删除操作,二叉搜索树可能会失去平衡,甚至可能退化为单链表,造成搜索效率低。

C++如何实现AVL树

AVL Tree 是一个「加上了额外平衡条件」的二叉搜索树,其平衡条件的建立是为了确保整棵树的深度为 O(log2N)。

AVL Tree 要求任何节点的左右子树高度相差最多为 1。当违反该规定时,就需要进行旋转来保证该规定。

AVL 树的实现

节点的定义

AVL 树节点的定义比一般的二叉搜索树复杂,它需要额外一个 parent 指针,方便后续旋转。并在每个节点中引入平衡因子,便于判断是否需要旋转。

/// @brief AVL 树节点结构/// @tparam K 节点的 key 值/// @tparam V 节点的 value 值template <class K, class V>struct AVLTreeNode {AVLTreeNode(const pair<K, V>& kv) : _kv(kv), _parent(nullptr), _left(nullptr), _right(nullptr), _bf(0){}pair<K, V> _kv;AVLTreeNode<K, V>* _parent;AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;    // 左右子树高度相同平衡因子为:0    // 左子树高平衡因子为负    // 右子树高平衡因子为正int _bf;};

接口总览

template<class K, class V>class AVLTree {typedef AVLTreeNode<K, V> Node;public:Node* Find(const K& key);bool Insert(const pair<K, V>& kv);private:void RotateR(Node* parent);void RotateL(Node* parent);void RotateLR(Node* parent);void RotateRL(Node* parent);private:Node* _root = nullptr;};

查找

AVL 树的查找和普通的搜索二叉树一样:

  • 若 key 值大于当前节点的值,在当前节点的右子树中查找

  • 若 key 值小于当前节点的值,在当前节点的左子树中查找

  • 若 key 值等于当前节点的值,返回当前节点的地址

  • 若找到空,查找失败,返回空指针

/// @brief 查找指定 key 值/// @param key 要查找的 key/// @return 找到返回节点的指针,没找到返回空指针Node* Find(const K& key) {    Node* cur = _root;    while (cur != nullptr) {        // key 值与当前节点值比较        if (key > cur->_kv.first) {            cur = cur->_right;        } else if (key < cur->_kv.first) {            cur = cur->_left;        } else {            return cur;        }    }    return nullptr;}

插入

AVL 的插入整体分为两步:

  • 按照二叉搜索树的方式将节点插入

  • 调整节点的平衡因子

平衡因子是怎么调整的?

设新插入的节点为 pCur,新插入节点的父节点为 pParent。在插入之前,pParent 的平衡因子有三种可能:0、-1、1。

插入分为两种:

  • pCur 插入到 pParent 的左侧,将 pParent 的平衡因子减 1

  • pCur 插入到 pParent 的右侧,将 pParent 的平衡因子加 1

此时,pParent 的平衡因子可能有三种情况:0、正负 1、正负 2。

  • 0:说明插入之前是正负 1,插入后被调整为 0,满足 AVL 性质插入成功

  • 正负 1:说明插入之前是 0,插入后被调整为正负 1,此时 pParent 变高,需要继续向上更新

  • 正负 2:说明插入之前是正负 1,插入后被调整为正负 2,此时破坏了规定,需要旋转处理

/// @brief 插入指定节点/// @param kv 待插入的节点/// @return 插入成功返回 true,失败返回 falsebool Insert(const pair<K, V>& kv) {    if (_root == nullptr) {        _root = new Node(kv);        return true;    }    // 先找到要插入的位置    Node* parent = nullptr;    Node* cur = _root;    while (cur != nullptr) {        if (kv.first > cur->_kv.first) {            parent = cur;            cur = cur->_right;        } else if (kv.first < cur->_kv.first) {            parent = cur;            cur = cur->_left;        } else {            // 已经存在,插入失败            return false;        }    }    // 将节点插入    cur = new Node(kv);    if (kv.first > parent->_kv.first) {        parent->_right = cur;        cur->_parent = parent;    } else {        parent->_left = cur;        cur->_parent = parent;    }    // 更新平衡因子,直到正常    while (parent != nullptr) {        // 调整父亲的平衡因子        if (parent->_left == cur) {            --parent->_bf;        } else {            ++parent->_bf;        }        if (parent->_bf == 0) {            // 此时不需要再继续调整了,直接退出            break;        } else if (parent->_bf == 1 || parent->_bf == -1) {            // 此时需要继续向上调整            cur = parent;            parent = parent->_parent;        } else if (parent->_bf == 2 || parent->_bf == -2) {            // 此时需要旋转处理            if (parent->_bf == -2 && cur->_bf == -1) {                RotateR(parent);            } else if (parent->_bf == 2 && cur->_bf == 1) {                RotateL(parent);            } else if (parent->_bf == -2 && cur->_bf == 1) {                RotateLR(parent);            } else if (parent->_bf == 2 && cur->_bf == -1) {                RotateRL(parent);            } else {                assert(false);            }            // 旋转完了就平衡了,直接退出            break;        } else {            // 此时说明之前就处理错了            assert(false);        } // end of if (parent->_bf == 0)    } // end of while (parent != nullptr)    return true;}

旋转

假设平衡因子为正负 2 的节点为 X,由于节点最多拥有两个子节点,因此可以分为四种情况:

  • 插入点位于 X 的左子节点的左子树&mdash;&mdash;左左:右单旋

  • 插入点位于 X 的左子节点的右子树&mdash;&mdash;左右:左右双旋

  • 插入点位于 X 的右子节点的右子树&mdash;&mdash;右右:左单旋

  • 插入点位于 X 的右子节点的左子树&mdash;&mdash;右左:右左双旋

C++如何实现AVL树

右单旋

C++如何实现AVL树

假设平衡因子为正负 2 的节点为 parent,parent 的父节点为 pParent,parent 的左子树为 subL,subL 的右子树为 subLR。

右单旋的操作流程:

  • 让 subLR 作为 parent 的左子树

  • 让 parent 作为 subL 的右子树

  • 让 subL 作为整个子树的新根

  • 更新平衡因子

/// @brief 进行右单旋/// @param parent 平衡因子为正负 2 的节点void RotateR(Node* parent) {    Node* pParent = parent->_parent;    Node* subL = parent->_left;    Node* subLR = parent->_left->_right;    // 更改链接关系    // 1. subLR 作为 parent 的左子树    parent->_left = subLR;    if (subLR != nullptr) {        subLR->_parent = parent;    }    // 2. parent 作为 subL 的右子树    subL->_right = parent;    parent->_parent = subL;    // 3. subL 作为整个子树的新根    if (parent == _root) {        // parent 为 _root,此时令 subL 为 _root        _root = subL;        subL->_parent = nullptr;    } else {        // parent 不为 _root,pParent 也就不为空        if (parent == pParent->_left) {            pParent->_left = subL;        } else {            pParent->_right = subL;        }        subL->_parent = pParent;    }    // 4. 更新平衡因子    // 观察上图明显可知    subL->_bf = 0;    parent->_bf = 0;}

左单旋

左单旋与右单旋类似,只是方向不同。

假设平衡因子为正负 2 的节点为 parent,parent 的父节点为 pParent,parent 的右子树为 subR,subR 的左子树为 subRL。

左单旋的操作流程:

  • 让 subRL 作为 parent 的右子树

  • 让 parent 作为 subR 的左子树

  • 让 subR 作为整个子树的新根

  • 更新平衡因子

/// @brief 进行左单旋/// @param parent 平衡因子为正负 2 的节点void RotateL(Node* parent) {    Node* pParetn = parent->_parent;    Node* subR = parent->_right;    Node* subRL = parent->_right->_left;    // 更改链接关系    // 1. subRL 作为 parent 的右子树    parent->_right = subRL;    if (subRL != nullptr) {        subRL->_parent = parent;    }    // 2. parent 作为 subR 的左子树    subR->_left = parent;    parent->_parent = subR;    // 3. subR 作为整个子树的新根    if (parent == _root) {        _root = subR;        subR->_parent = nullptr;    } else {        if (parent == pParetn->_left) {            pParetn->_left = subR;        } else {            pParetn->_right = subR;        }        subR->_parent = pParetn;    }    // 4. 更新平衡因子    subR->_bf = 0;    parent->_bf = 0;}

左右双旋

C++如何实现AVL树

假设平衡因子为正负 2 的节点为 parent,parent 的左子树为 subL,subL 的右子树为 subLR。

左右双旋就是对 subL 进行一次左单旋,对 parent 进行一次右单旋。双旋也就完成了,要注意的是双旋后平衡因子的更新。

此时分三种情况:

新插入的节点是 subLR 的右子树

C++如何实现AVL树

新插入的节点是 subLR 的左子树

C++如何实现AVL树

新插入的是 subLR

C++如何实现AVL树

结合上述情况,写出如下代码:

/// @brief 进行左右双旋/// @param parent 平衡因子为正负 2 的节点void RotateLR(Node* parent) {    Node* subL = parent->_left;    Node* subLR = parent->_left->_right;    int bf = subLR->_bf;    RotateL(subL);    RotateR(parent);    if (bf == 1) {        // 新插入节点是 subLR 的右子树        parent->_bf = 0;        subL->_bf = -1;        subLR->_bf = 0;    } else if (bf == -1) {        // 新插入的节点是 subLR 的左子树        parent->_bf = 1;        subL->_bf = 0;        subLR->_bf = 0;    } else if (bf == 0) {        // 新插入的节点是 subLR        parent->_bf = 0;        subL->_bf = 0;        subLR->_bf = 0;    } else {        assert(false);    }}

右左双旋

假设平衡因子为正负 2 的节点为 parent,parent 的右子树为 subR,subR 的左子树为 subRL。

右左双旋就是对 subR 进行一次右单旋,对 parent 进行一次左单旋。流程和左右双旋一样,这里就不过多介绍了。

void RotateRL(Node* parent) {    Node* subR = parent->_right;    Node* subRL = parent->_right->_left;    int bf = subRL->_bf;    RotateR(subR);    RotateL(parent);    if (bf == 1) {        // 新插入节点是 subRL 的右子树        parent->_bf = -1;        subR->_bf = 0;        subRL->_bf = 0;    } else if (bf == -1) {        // 新插入的节点是 subRL 的左子树        parent->_bf = 0;        subR->_bf = 1;        subRL->_bf = 0;    } else if (bf == 0) {        // 新插入的节点是 subRL        parent->_bf = 0;        subR->_bf = 0;        subRL->_bf = 0;    } else {        assert(false);    }}

“C++如何实现AVL树”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

C++如何实现AVL树

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

C++如何实现AVL树

本篇内容介绍了“C++如何实现AVL树”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!AVL 树的概念也许因为插入的值不够随机,也许因为经过某
2023-07-05

C++怎么实现AVL树

这篇文章主要介绍“C++怎么实现AVL树”,在日常操作中,相信很多人在C++怎么实现AVL树问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++怎么实现AVL树”的疑惑有所帮助!接下来,请跟着小编一起来学习吧
2023-06-22

C++数据结构之AVL树如何实现

这篇文章主要讲解了“C++数据结构之AVL树如何实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++数据结构之AVL树如何实现”吧!1.概念(1)二叉搜索树的缺点要手撕AVL树,我们首先
2023-07-02

怎么在C++中实现AVL树

怎么在C++中实现AVL树?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。AVL树的介绍AVL树是一种自平衡的二叉搜索树,它通过单旋转(single rotate)和双旋转(do
2023-06-15

C++实现AVL树的示例详解

AVLTree是一个「加上了额外平衡条件」的二叉搜索树,其平衡条件的建立是为了确保整棵树的深度为O(log_2N),本文主要介绍了AVL树的实现,需要的可以参考一下
2023-03-03

如何在Python中实现avl树运算

Python执行avl树,代码详情:import sys#创建树节点class TreeNode(object):def __init__(self,key):self.key=keyself.left=Noneself.rig
如何在Python中实现avl树运算
2024-01-23

AVL树数据结构输入与输出怎么实现

本篇内容介绍了“AVL树数据结构输入与输出怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!AVL树(平衡二叉树):AVL树本质上是一颗
2023-06-30

C#如何实现二叉查找树

这篇文章主要介绍了C#如何实现二叉查找树的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C#如何实现二叉查找树文章都会有所收获,下面我们一起来看看吧。对于符号表,要支持高效的插入操作,就需要一种链式结构。但单链表
2023-06-30

C#如何实现平衡查找树

这篇文章主要介绍了C#如何实现平衡查找树的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C#如何实现平衡查找树文章都会有所收获,下面我们一起来看看吧。1. 2-3查找树为了保证查找树的平衡性,我们需要一些灵活性,
2023-06-30

如何用C语言实现圣诞树

这篇文章主要介绍“如何用C语言实现圣诞树”,在日常操作中,相信很多人在如何用C语言实现圣诞树问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何用C语言实现圣诞树”的疑惑有所帮助!接下来,请跟着小编一起来学习吧
2023-06-22

C++如何实现二叉树的遍历

本篇内容介绍了“C++如何实现二叉树的遍历”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!二叉树的遍历Q:什么是二叉树的遍历?A:二叉树的遍历
2023-06-30

C++如何实现验证二叉搜索树

本文小编为大家详细介绍“C++如何实现验证二叉搜索树”,内容详细,步骤清晰,细节处理妥当,希望这篇“C++如何实现验证二叉搜索树”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。验证二叉搜索树Example 1:In
2023-06-19

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录