广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++如何实现AVL树
  • 878
分享到

C++如何实现AVL树

2023-07-05 08:07:50 878人浏览 泡泡鱼
摘要

本篇内容介绍了“c++如何实现AVL树”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!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树”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: C++如何实现AVL树

本文链接: https://www.lsjlt.com/news/350676.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

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

下载Word文档
猜你喜欢
  • C++如何实现AVL树
    本篇内容介绍了“C++如何实现AVL树”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!AVL 树的概念也许因为插入的值不够随机,也许因为经过某...
    99+
    2023-07-05
  • C++怎么实现AVL树
    这篇文章主要介绍“C++怎么实现AVL树”,在日常操作中,相信很多人在C++怎么实现AVL树问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++怎么实现AVL树”的疑惑有所帮助!接下来,请跟着小编一起来学习吧...
    99+
    2023-06-22
  • 怎么在C++中实现AVL树
    怎么在C++中实现AVL树?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。AVL树的介绍AVL树是一种自平衡的二叉搜索树,它通过单旋转(single rotate)和双旋转(do...
    99+
    2023-06-15
  • C++数据结构之AVL树如何实现
    这篇文章主要讲解了“C++数据结构之AVL树如何实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++数据结构之AVL树如何实现”吧!1.概念(1)二叉搜索树的缺点要手撕AVL树,我们首先...
    99+
    2023-07-02
  • C++实现AVL树的完整代码
    AVL树的介绍 AVL树是一种自平衡的二叉搜索树,它通过单旋转(single rotate)和双旋转(double rotate)的方式实现了根节点的左子树与右子树的高度差不超过1...
    99+
    2022-11-12
  • C++实现AVL树的示例详解
    目录AVL 树的概念AVL 树的实现节点的定义接口总览插入旋转AVL 树的概念 也许因为插入的值不够随机,也许因为经过某些插入或删除操作,二叉搜索树可能会失去平衡,甚至可能退化为单链...
    99+
    2023-03-03
    C++实现AVL树 C++ AVL树
  • C++数据结构之AVL树的实现
    目录1.概念(1)二叉搜索树的缺点(2)定义节点2.插入(1)拆分(2)找节点与插节点(3)更新平衡因子与旋转3.判断4.完整代码及测试代码完整代码测试代码1.概念 (1)二叉搜索树...
    99+
    2022-11-13
  • C++实现AVL树的基本操作指南
    目录AVL树的概念AVL树的插入AVL树的四种旋转右单旋左单旋左右双旋右左双旋查找其他接口析构函数拷贝构造拷贝赋值总结AVL树的概念 二叉搜索树虽可以缩短查找的效率,但如果数据有序或...
    99+
    2022-11-12
  • AVL树数据结构输入与输出怎么实现
    本篇内容介绍了“AVL树数据结构输入与输出怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!AVL树(平衡二叉树):AVL树本质上是一颗...
    99+
    2023-06-30
  • 图解AVL树数据结构输入与输出及实现示例
    目录AVL树(平衡二叉树):AVL树的作用:AVL树的基本操作:AVL树的插入,单旋转的第一种情况---右旋:AVL树的插入,单旋转的第二种情况---左旋:AVL树的插入,双旋转的第...
    99+
    2022-11-13
  • C++如何实现二叉树链表
    目录C++二叉树链表C++二叉树转链表C++二叉树链表 Node.h #ifndef NODE_H #define NODE_H #include <iostream> ...
    99+
    2022-11-13
  • C#如何实现二叉查找树
    这篇文章主要介绍了C#如何实现二叉查找树的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C#如何实现二叉查找树文章都会有所收获,下面我们一起来看看吧。对于符号表,要支持高效的插入操作,就需要一种链式结构。但单链表...
    99+
    2023-06-30
  • C#如何实现平衡查找树
    这篇文章主要介绍了C#如何实现平衡查找树的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C#如何实现平衡查找树文章都会有所收获,下面我们一起来看看吧。1. 2-3查找树为了保证查找树的平衡性,我们需要一些灵活性,...
    99+
    2023-06-30
  • C++实现LeetCode(208.实现字典树(前缀树))
    [LeetCode] 208. Implement Trie (Prefix Tree) 实现字典树(前缀树) Implement a trie with insert,&...
    99+
    2022-11-12
  • C++如何实现二叉树的遍历
    本篇内容介绍了“C++如何实现二叉树的遍历”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!二叉树的遍历Q:什么是二叉树的遍历?A:二叉树的遍历...
    99+
    2023-06-30
  • 如何用C语言实现圣诞树
    这篇文章主要介绍“如何用C语言实现圣诞树”,在日常操作中,相信很多人在如何用C语言实现圣诞树问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何用C语言实现圣诞树”的疑惑有所帮助!接下来,请跟着小编一起来学习吧...
    99+
    2023-06-22
  • C++如何实现验证二叉搜索树
    本文小编为大家详细介绍“C++如何实现验证二叉搜索树”,内容详细,步骤清晰,细节处理妥当,希望这篇“C++如何实现验证二叉搜索树”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。验证二叉搜索树Example 1:In...
    99+
    2023-06-19
  • C#实现二叉查找树
    目录1.实现API1.数据结构2.查找3.插入4.分析有序性相关的方法和删除操作1.最大键和最小键2.向上取整和向下取整3.选择操作4.排名5.删除最大键和删除最小键6.删除操作7....
    99+
    2022-11-13
  • C#实现平衡查找树
    目录1. 2-3查找树1.查找2.向 2- 结点中插入新键3.向一棵只含有一个 3- 结点的树中插入新键4.向一个父结点为 2- 结点的 3- 结点中插入新键5.向一个父结点为 3-...
    99+
    2022-11-13
  • C++怎么实现线段树
    本篇内容介绍了“C++怎么实现线段树”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!目录应用场景算法思想查询操作修改操作算法实现建树查询修改应...
    99+
    2023-06-20
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作