【C++】-- AVL树详解(二)

简介: 【C++】-- AVL树详解

②左单旋

新节点插入到较高右子树的右侧,即右右-----左单旋

插入新节点前,AVL树是平衡的,新节点插入到20的右子树,那么20的右子树增加了一层,导致以10为根的二叉树不平衡。为了让10平衡,只能让10的右子树的高度减小一层,并把20的左子树的高度增加一层。

因此,要把20的右子树往上提,把10转下来,因为10比20小,只能把10放在20的左子树,20的左子树比20小,比10大,因此只能把20的左子树放在10的右子树。再更新节点平衡因子。

抽象图:

需要考虑的情况:

(1)20的左孩子可能存在,也可能不存在

(2)10可能是根节点,也可能是子树;如果是根节点,旋转后,要更新根节点。如果是子树,可能是左子树也可能是右子树,就把10原来的父亲的左或右指向20。

1.  void RotateL(Node* parent)
2.  {
3.    Node* subR = parent->_right;
4.    Node* subRL = nullptr;
5. 
6.    if (subR)
7.    {
8.      subRL = subR->_left;
9.    }
10. 
11.     //1.右子树的左子树变我的右子树
12.     parent->_right = subRL;
13. 
14.     if (subRL)
15.     {
16.       subRL->_parent = parent;
17.     }
18. 
19.     //2.右子树变父亲
20.     subR->_left = parent;
21.     Node* parentParent = parent->_parent;
22.     parent->_parent = subR;
23. 
24.     if (parent == _root)//parent是根
25.     {
26.       _root = parent;
27.       _root->_parent = nullptr;
28.     }
29.     else//parent不是根,是子树
30.     {
31.       if (parentParent->_left == parent)
32.       {
33.         //parent是自己父亲的左子树,将subR作为parent父亲的左孩子
34.         parentParent->_left = subR;
35.       }
36.       else
37.       {
38.         //parent是自己父亲的右子树,将subR作为parent父亲的右孩子
39.         parentParent->_right = subR;
40.       }
41. 
42.       //subR的父亲就是parent的父亲
43.       subR->_parent = parentParent;
44.     }
45. 
46.     //更新平衡因子
47.     subR->_bf = parent->_bf = 0;
48.   }

具象图:

h=0的情况:

10变成20的左子树,20的左子树为空,不考虑

h=1的情况:

10变成20的左子树,20的左子树变成10的右子树

h=2的情况:

10变成20的左子树,20的左子树变成10的右子树

③左右双旋

新节点插入较高左子树的右侧---左右:先左单旋再右单旋

插入新节点前,AVL树是平衡的,新节点插入到20的左子树,那么20的左子树增加了一层,导致以30为根的二叉树不平衡。为了让30平衡,只能让30的左子树的高度减小一层。

现在30左子树的高度是h+1,但是现在不能把10进行右单旋,因为要把10右单旋,那么20和30都必须处于10的右子树,这办不到。因此要分为两步:

(1)先把10左单旋

(2)再把20右单旋

1. void RotateLR(Node* parent)
2.  {
3.    Node* subL = parent->_left;
4.    Node* subLR = subL->_right;
5.    int bf = 0;
6. 
7.    if (subLR)
8.    {
9.      bf = subLR->_bf;
10.     }
11. 
12. 
13.     RotateL(parent->_left);
14.     RotateR(parent);
15. 
16.     //平衡因子调节还需要分析
17.     if (bf == -1)
18.     {
19.       subL->_bf = 0;
20.       parent->_bf = 1;
21.       subLR->_bf = 0;
22.     }
23.     else if (bf == 1)
24.     {
25.       parent->_bf = 0;
26.       subL->_bf = -1;
27.       subLR->_bf = 0;
28.     }
29.     else if (bf == 0)
30.     {
31.       parent->_bf = 0;
32.       subL->_bf = 0;
33.       subLR->_bf = 0;
34.     }
35.     else
36.     {
37.       assert(false);
38.     }
39.   }

④右左双旋

新节点插入较高右子树的左侧---右左:先右单旋再左单旋

插入新节点前,AVL树是平衡的,新节点插入到30的右子树,那么30的右子树增加了一层,导致以10为根的二叉树不平衡。为了让10平衡,只能让10的右子树的高度减小一层。

现在10右子树的高度是h+1,但是现在不能把30进行左单旋,因为要把30左单旋,那么10和20都必须处于30的左子树,这办不到。因此要分为两步:

(1)先把20右单旋

(2)再把10左单旋

1.  void RotateRL(Node* parent)
2.  {
3.    Node* subR = parent->_right;
4.    Node* subRL = subR->_left;
5.    int bf = 0;
6. 
7.    if (subRL)
8.    {
9.      bf = subRL->_bf;
10.     }
11. 
12. 
13.     //先右旋再左旋
14.     RotateR(parent->_right);
15.     RotateL(parent);
16. 
17.     //平衡因子调节还需要具体分析
18.     if (bf == 1)
19.     {
20.       parent->_bf = -1;
21.       subR->_bf = 0;      
22.       subRL->_bf = 0;
23.     }
24.     else if (bf == -1)
25.     {
26.       parent->_bf = 0;
27.       subR->_bf = 1;
28.       subRL->_bf = 0;
29.     }
30.     else if (bf == 0)
31.     {
32.       parent->_bf = 0;
33.       subR->_bf = 0;
34.       subRL->_bf = 0;
35.     }
36.     else
37.     {
38.       assert(false);
39.     }
40.   }

四、AVL树查找

查找比较简单:

(1)如果key比当前节点大,那就继续向右查找;

(2)如果key比当前节点小,那就继续向左查找;

(3)如果key恰好等于当前节点,找到了

1.  Node* Find(const K& key)
2.  {
3.    Node* cur = _root;
4.    while (cur)
5.    {
6.      if (cur.first < key)//比当前节点大,向右查找
7.      {
8.        cur = cur->_right;
9.      }
10.       else if (cur.first > key)//比当前节点小,向左查找
11.       {
12.         cur = cur->_left;
13.       }
14.       else//等于当前节点,找到了
15.       {
16.         return cur;
17.       }
18.     }
19.     return nullptr;
20.   }

五、AVL树高度

计算树高度肯定要递归计算:

(1)计算左右子树的高度

(2)谁的高度大,那AVL树的高度就为较高子树的高度+1

1.  //求树的高度
2.  int Height(Node* root)
3.  {
4.    if (root == nullptr)
5.    {
6.      return 0;
7.    }
8. 
9.    int leftHeight = Height(root->_left);
10.     int rightHeight = Height(root->_right);
11. 
12.     return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
13.   }

六、判断是否为AVL树

检查树是否是AVL树:

(1)获取左右子树高度

(2)根据左右子树高度计算平衡因子

(3)当平衡因子<=2 || -2时就是平衡的

1.  bool _IsBanlance(Node* root)
2.  {
3.    if (root == nullptr)
4.    {
5.      return true;
6.    }
7. 
8.    int leftHeight = Height(root->_left);
9.    int rightHeight = Height(root->_right);
10. 
11.     //检查平衡因子是否正确
12.     if (rightHeight - leftHeight != root->_bf)
13.     {
14.       cout << "平衡因子异常:" << root->_kv.first << endl;
15.       return false;
16.     }
17. 
18.     return abs(rightHeight - leftHeight) < 2
19.       && _IsBanlance(root->_left)
20.       && _IsBanlance(root->_right);
21.   }
22. 
23.   //判断是否是AVL树
24.   bool IsAVLTree()
25.   {
26.     return _IsBanlance(_root);
27.   }

七、AVL树遍历

遍历也很简单:递归遍历左子树和右子树即可

1.  void _InOrder(Node* root)
2.  {
3.    if (root == nullptr)
4.    {
5.      return;
6.    }
7. 
8.    _InOrder(root->_left);
9.    cout << root->_kv.first << ":" << root->_kv.second << endl;
10.     _InOrder(root->_right);
11.   }
12. 
13.   void InOrder()
14.   {
15.     _InOrder(_root);
16.   }

八、时间复杂度

AVL树的操作时,需要找到位置,因此时间复杂度为高度次,即O() 。

相关文章
|
5月前
|
监控 算法 数据处理
基于 C++ 的 KD 树算法在监控局域网屏幕中的理论剖析与工程实践研究
本文探讨了KD树在局域网屏幕监控中的应用,通过C++实现其构建与查询功能,显著提升多维数据处理效率。KD树作为一种二叉空间划分结构,适用于屏幕图像特征匹配、异常画面检测及数据压缩传输优化等场景。相比传统方法,基于KD树的方案检索效率提升2-3个数量级,但高维数据退化和动态更新等问题仍需进一步研究。未来可通过融合其他数据结构、引入深度学习及开发增量式更新算法等方式优化性能。
152 17
|
9月前
|
存储 C++
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
【数据结构——树】哈夫曼树(头歌实践教学平台习题)【合集】目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:任务描述 本关任务:编写一个程序构建哈夫曼树和生成哈夫曼编码。 相关知识 为了完成本关任务,你需要掌握: 1.如何构建哈夫曼树, 2.如何生成哈夫曼编码。 测试说明 平台会对你编写的代码进行测试: 测试输入: 1192677541518462450242195190181174157138124123 (用户分别输入所列单词的频度) 预
209 14
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
|
9月前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
193 12
|
9月前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
169 10
|
9月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
279 3
|
11月前
|
存储 C++
【C++】AVL树
AVL树是一种自平衡二叉搜索树,由Georgy Adelson-Velsky和Evgenii Landis提出。它通过确保任意节点的两子树高度差不超过1来维持平衡,支持高效插入、删除和查找操作,时间复杂度为O(log n)。AVL树通过四种旋转操作(左旋、右旋、左-右旋、右-左旋)来恢复树的平衡状态,适用于需要频繁进行数据操作的场景。
340 2
|
存储 C++
【C++】AVL树
AVL树是一种自平衡二叉搜索树:它以苏联科学家Georgy Adelson-Velsky和Evgenii Landis的名字命名。
127 2
|
C++ 容器
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树
122 5
|
C++
【C++】手撕AVL树(下)
【C++】手撕AVL树(下)
119 1
|
Java C++ Python
【C++】手撕AVL树(上)
【C++】手撕AVL树(上)
141 0