前言

  今天要介绍几种高级数据结构AVL树,介绍之前AVL,会先说明平衡二叉树,并将树的学习路线进行总结,并介绍维持平衡的方法:右旋转、左旋转。

  一、树学习路线

  1、路线总结

  总结了一下树的学习路线,如下图:

  

  2、说明

  上面这个图要从上往下进行一步一步学习;首先,从二叉树开始学习,要对树的一些概念有一些基本了解,如树的左孩子和右孩子等,然后对树的遍历方法:先序、中序和后序遍历都熟练掌握,有精力再把层序遍历掌握;

  接下来,大部分的树,都是在二叉树的基础上加了许多特性而形成的,所以二叉树是基础,如二叉搜索树,任意一个节点都比左子树大,都比右子树小,主要用于解决查找问题,对二分查找法有一个基本了解,还有一个特性,二分搜索树的中序遍历:数据就从从小到大进行排序了。

  AVL树,是今天要讲的话题,下面会详细进行讲解。

  红黑树应该是大名鼎鼎了,都应该听过了,之后我会专门介绍。

  trie树就不再是二叉树了,是多叉树了,之后也会讲解。

  二、平衡二叉树

  1、定义

  平衡二叉树,首先是二叉树,并且对于任意一个节点,左子树和右子树的高度差不能超过1。

  2、意义

  有了二分查找树为什么还要平衡二叉树呢?这篇对二分查找树进行了详细介绍,并对先序、中序和后序进行了明确说明,可以参考:https://www.cnblogs.com/liudw-0215/p/9835691.html,因为二叉树有一个弊端就是会退化为链表,就是只有左子树或右子树有节点,这样查询效率就会变低了。所以,就需要“平衡”这个概念了。

  3、平衡因子

  先画个图,进行说明,不是平衡二叉树,只是为了说明问题,如下图:

  

  说明:如上图,树的高度从叶子节点开始,并且叶子节点高度是1;平衡因子就是用左子树高度减去右子树高度,例如:4这个节点,左子树2的高度为1,右子树没有则为0,所以4这个节点的平衡因子为1。

  三、AVL树

  1、定义

    AVL树是自平衡二分搜索树,既具有平衡性和二分性。

   2、构建AVL树类

    是在二分搜索树的基础上进行修改并维持“平衡”这个特性的,首先,来看下AVL树的类,如下:

  1. #ifndef AVLTREE_AVLTREE_H
  2. #define AVLTREE_AVLTREE_H
  3.  
  4. #include <algorithm>
  5. #include <iostream>
  6. #include <vector>
  7.  
  8. template<typename Key, typename Value>
  9. class AVLTree {
  10. private:
  11. struct Node {
  12. Key key;
  13. Value value;
  14. Node *left;
  15. Node *right;
  16. int height; //用于标注高度,计算平衡因子
  17.  
  18. Node(Key key, Value value) {
  19. this->key = key;
  20. this->value = value;
  21. this->left = this->right = nullptr;
  22. height = ;
  23. }
  24.  
  25. Node(Node *node) {
  26. this->key = node->key;
  27. this->value = node->value;
  28. this->left = node->left;
  29. this->right = node->right;
  30. this->height = node->height;
  31. }
  32. };
  33.  
  34. Node *root;
  35. int size;
  36.  
  37. public:
  38.  
  39. AVLTree() {
  40. root = nullptr;
  41. size = ;
  42. }
  43.  
  44. ~AVLTree() {
  45. destroy(root);
  46. }
  47.  
  48. int getSize() {
  49. return size;
  50. }
  51.  
  52. int isEmpty() {
  53. return size == ;
  54. }
  55.  
  56. int getHeight(Node *node) { //获取高度
  57. if (node == nullptr) {
  58. return ;
  59. }
  60. return node->height;
  61. }
  62.  
  63. int getBalanceFactor(Node *node) { //获取平衡因子
  64. if (node == nullptr) {
  65. return ;
  66. }
  67. return getHeight(node->left) - getHeight(node->right);
  68. }
  69.  
  70. bool isBST() {
  71. std::vector<Key> keys;
  72. inOrder(root, keys);
  73. for (int i = ; i < keys.size(); ++i) {
  74. if (keys.at(i - ) < keys.at(i)) {
  75. return false;
  76. }
  77. }
  78. return true;
  79. }
  80.  
  81. bool isBalanced() {
  82. return isBalanced(root);
  83. }
  84.  
  85. void add(Key key, Value value) {
  86. root = add(root, key, value);
  87. }
  88.  
  89. bool contains(Key key) {
  90. return getNode(root, key) != nullptr;
  91. }
  92.  
  93. Value *get(Key key) {
  94. Node *node = getNode(root, key);
  95. return node == nullptr ? nullptr : &(node->value);
  96. }
  97.  
  98. void set(Key key, Value newValue) {
  99. Node *node = getNode(root, key);
  100. if (node != nullptr) {
  101. node->value = newValue;
  102. }
  103. }
  104.  
  105. // 从二叉树中删除键值为key的节点
  106. Value *remove(Key key) {
  107. Node *node = getNode(root, key);
  108. if (node != nullptr) {
  109. root = remove(root, key);
  110. return &(node->value);
  111. }
  112. return nullptr;
  113. }
  114.  
  115. private:
  116.  
  117. // 向以node为根的二叉搜索树中,插入节点(key, value)
  118. // 返回插入新节点后的二叉搜索树的根
  119. Node *add(Node *node, Key key, Value value) {
  120. if (node == nullptr) {
  121. size++;
  122. return new Node(key, value);
  123. }
  124. if (key == node->key) {
  125. node->value = value;
  126. } else if (key < node->key) {
  127. node->left = add(node->left, key, value);
  128. } else {
  129. node->right = add(node->right, key, value);
  130. }
  131. node->height = + std::max(getHeight(node->left), getHeight(node->right));
  132. int balanceFactor = getBalanceFactor(node);
  133. if (std::abs(balanceFactor) > ) {
  134. std::cout << "unbalanced : " << balanceFactor;
  135. }
  136. return node;
  137. }
  138.  
  139. // 在以node为根的二叉搜索树中查找key所对应的Node
  140. Node *getNode(Node *node, Key key) {
  141. if (node == nullptr) {
  142. return nullptr;
  143. }
  144. if (key == node->key) {
  145. return node;
  146. } else if (key < node->key) {
  147. return getNode(node->left, key);
  148. } else {
  149. return getNode(node->right, key);
  150. }
  151. }
  152.  
  153. void destroy(Node *node) {
  154. if (node != nullptr) {
  155. destroy(node->left);
  156. destroy(node->right);
  157. delete node;
  158. size--;
  159. }
  160. }
  161.  
  162. // 在以node为根的二叉搜索树中,返回最小键值的节点
  163. Node *minimum(Node *node) {
  164. if (node->left == nullptr)
  165. return node;
  166. return minimum(node->left);
  167. }
  168.  
  169. // 在以node为根的二叉搜索树中,返回最大键值的节点
  170. Node *maximum(Node *node) {
  171. if (node->right == nullptr)
  172. return node;
  173. return maximum(node->right);
  174. }
  175.  
  176. // 删除掉以node为根的二分搜索树中的最小节点
  177. // 返回删除节点后新的二分搜索树的根
  178. Node *removeMin(Node *node) {
  179. if (node->left == nullptr) {
  180. Node *rightNode = node->right;
  181. delete node;
  182. size--;
  183. return rightNode;
  184. }
  185.  
  186. node->left = removeMin(node->left);
  187. return node;
  188. }
  189.  
  190. // 删除掉以node为根的二分搜索树中的最大节点
  191. // 返回删除节点后新的二分搜索树的根
  192. Node *removeMax(Node *node) {
  193. if (node->right == nullptr) {
  194. Node *leftNode = node->left;
  195. delete node;
  196. size--;
  197. return leftNode;
  198. }
  199.  
  200. node->right = removeMax(node->right);
  201. return node;
  202. }
  203.  
  204. // 删除掉以node为根的二分搜索树中键值为key的节点
  205. // 返回删除节点后新的二分搜索树的根
  206. Node *remove(Node *node, Key key) {
  207. if (node == nullptr) {
  208. return nullptr;
  209. }
  210. if (key < node->key) {
  211. node->left = remove(node->left, key);
  212. return node;
  213. } else if (key > node->key) {
  214. node->right = remove(node->right, key);
  215. return node;
  216. } else {
  217. if (node->left == nullptr) {
  218. Node *rightNode = node->right;
  219. delete node;
  220. size--;
  221. return rightNode;
  222. }
  223.  
  224. if (node->right == nullptr) {
  225. Node *leftNode = node->left;
  226. delete node;
  227. size--;
  228. return leftNode;
  229. }
  230.  
  231. Node *successor = new Node(minimum(node->right));
  232. //Node *precursor = new Node(maximum(node->right));
  233. size++;
  234.  
  235. successor->right = removeMin(node->right);
  236. successor->left = node->left;
  237. //precursor->left = removeMax(node->left);
  238. //precursor->right = node->right;
  239.  
  240. delete node;
  241. size--;
  242.  
  243. return successor;
  244. //return precursor;
  245. }
  246. }
  247.  
  248. void inOrder(Node *node, std::vector<Key> keys) {
  249. if (node == nullptr) {
  250. return;
  251. }
  252. inOrder(node->left, keys);
  253. keys.push_back(node->key);
  254. inOrder(node->right, keys);
  255. }
  256.  
  257. bool isBalanced(Node *node) {
  258. if (node == nullptr) {
  259. return true;
  260. }
  261.  
  262. int balanceFactor = getBalanceFactor(node);
  263. if (std::abs(balanceFactor) > ) {
  264. return false;
  265. }
  266.  
  267. return isBalanced(node->left) && isBalanced(node->right);
  268. }
  269. };
  270.  
  271. #endif //AVLTREE_AVLTREE_H

  增加height属性,用于记录每个节点的高度,并计算平衡因子;

  3、获取节点高度

  把height属性返回就可以了:

  1. int getHeight(Node *node) { //获取高度
  2. if (node == nullptr) {
  3. return ;
  4. }
  5. return node->height;
  6. }

  4、获取平衡因子

  将左子树高度减去右子树高度即可,但注意:不要区绝对值,因为之后的旋转要判断左子树还是右子树的高度高,代码如下:

  

  1. int getBalanceFactor(Node *node) { //获取平衡因子
  2. if (node == nullptr) {
  3. return ;
  4. }
  5. return getHeight(node->left) - getHeight(node->right);
  6. }

  5、判断是不是平衡二叉树

  平衡因子大于1就不是平衡二叉树了,代码如下:

  

  1. bool isBalanced(Node *node) {
  2. if (node == nullptr) {
  3. return true;
  4. }
  5.  
  6. int balanceFactor = getBalanceFactor(node);
  7. if (std::abs(balanceFactor) > ) {
  8. return false;
  9. }
  10.  
  11. return isBalanced(node->left) && isBalanced(node->right);
  12. }
  13.  
  14. bool isBalanced() {
  15. return isBalanced(root);
  16. }

  四、AVL树的旋转

   1、什么时维护平衡?

  如下图,假如原来没有2这个节点,那么树是平衡二叉树,但插入2之后,就不再平衡了,这时就需要维护平衡了,大体上有4种情况需要维护平衡,来说明这一种。

    

    2、右旋转 LL

    将其中的部分节点抽离出来,如下图:

  

  说明:主要分为两步:

  第一步:将T3保存,然后将y以及孩子节点旋转到x的右孩子位置,相对于x,y是顺时针向右旋转的,所以叫右旋转;

  第二步:将T3移到y的左孩子位置

  最后,形成的二叉树符合二分和平衡两个性质,所以还是平衡二叉树。

  3、右旋转代码实现

  上图应该已经讲解的很明白了吧,代码如下:

  

  1. Node *rightRotate(Node *y) {
  2. Node *x = y->left; //存x
  3. Node *tmp = x->right; //将x的右孩子备份
  4. x->right = y; //将y右旋转到x的右孩子
  5. y->left = tmp; //将x的右孩子移到y的左侧
  6. y->height = std::max(getHeight(y->left), getHeight(y->right)) + ; //修改y高度,注意要先修改y的高度
  7. x->height = std::max(getHeight(x->left), getHeight(x->right)) + ; //修改x的高度
  8. return x;
  9. }

  4、左旋转 RR

  左旋转和右旋转很相似,只是方向不同,如下图:

  

  说明:相对于x,y是逆时针向左旋转,所以是左旋转

  5、左旋转代码实现

  左旋转代码跟右旋转很相似,代码如下:

  

  1. Node *leftRotate(Node *y){
  2. Node *x = y->right;
  3. Node *tmp = x->left;
  4. x->left = y;
  5. y->right = tmp;
  6. y->height = std::max(getHeight(y->left), getHeight(y->right)) + ;
  7. x->height = std::max(getHeight(x->left), getHeight(x->right)) + ;
  8. return x;
  9. }

  6、LR

  还有两种情况需要讨论,LL代表“左左”,LR代表“左右”,如下图:

  

  说明:借助左旋转将LR转为LL,再对LL进行右旋转就OK了,所以理解左、右旋转是基础!

  7、LR代码实现

  代码如下:

  

  1. if (balanceFactor > && getBalanceFactor(node->left) < ) { //LR
  2. node->left = leftRotate(node->left);
  3. return rightRotate(node);
  4. }

  8、RL

  最后一种情况RL,如下图:

  

  9、RL代码实现

  代码如下:

  

  1. if (balanceFactor < - && getBalanceFactor(node->right) > ) { //RL
  2. node->right = rightRotate(node->right);
  3. return leftRotate(node);
  4. }

  总结

  AVL树和平衡二叉树就比较难了,主要理解右旋转和左旋转,对之后理解红黑树有巨大作用!

  

AVL树和平衡二叉树 平衡因子 右旋转LL 左旋转RR LR RL的更多相关文章

  1. AVL树(自平衡二叉查找树)

    了解AVL树之前要先了解二叉查找树(BST),BST查找元素的时间复杂度平均是O(logN),最坏的情况是O(N),所有的元素都接在左子树(或者右子树)就相当于一串链表了.而AVL树会对子树过高的情况 ...

  2. AVL树(平衡二叉树)

    定义及性质 AVL树:AVL树是一颗自平衡的二叉搜索树. AVL树具有以下性质: 根的左右子树的高度只差的绝对值不能超过1 根的左右子树都是 平衡二叉树(AVL树) 百度百科: 平衡二叉搜索树(Sel ...

  3. D&F学数据结构系列——AVL树(平衡二叉树)

    AVL树(带有平衡条件的二叉查找树) 定义:一棵AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树. 为什么要使用AVL树(即为什么要给二叉查找树增加平衡条件),已经在我之前的博文中说到过 ...

  4. 第七章 二叉搜索树 (d1)AVL树:重平衡

  5. AVL树插入和删除

    一.AVL树简介 AVL树是一种平衡的二叉查找树. 平衡二叉树(AVL 树)是一棵空树,或者是具有下列性质的二叉排序树:    1它的左子树和右子树都是平衡二叉树,    2且左子树和右子树高度之差的 ...

  6. 使用C编程语言实现AVL树

    本文将介绍AVL树及其插入.删除操作,最后使用C编程语言实现基于平衡因子(balance factor)的AVL树. 什么是AVL树? AVL树(AVL tree)是前苏联计算机科学家Adelson- ...

  7. linux 内核数据结构之 avl树.

    转载: http://blog.csdn.net/programmingring/article/details/37969745 https://zh.wikipedia.org/wiki/AVL% ...

  8. 数据结构之AVL树

    AVL树是高度平衡的而二叉树.它的特点是:AVL树中任何节点的两个子树的高度最大差别为1. 旋转 如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡.这种失去平衡的可以概括为4种姿态:LL ...

  9. AVL树(三)之 Java的实现

    概要 前面分别介绍了AVL树"C语言版本"和"C++版本",本章介绍AVL树的Java实现版本,它的算法与C语言和C++版本一样.内容包括:1. AVL树的介绍 ...

随机推荐

  1. ajax 文件下载

    作为一个后端开发人员,使用java 生成文件,提供前端下载,这个问题倒不大,可是让我们自己去下载文件的时候,这个问题就大了,对不起,我只对前端一知半解,并不精通,谢谢!! 需求如下:前端检索数据,后台 ...

  2. C语言中free()函数释放struct结构体中的规律

    并不是什么新鲜的事情,不过值得注意.首先我们知道,在使用struct来定义并声明一个变量时,将会自动划分出一个连续的储存空间(虽然根据某些对齐原则会出现内存间隙,但是大体上来说还是连续的)这一块连续空 ...

  3. 黑洞版视频裂变程序【接口版】全新上线,全新UI,支持分享数据统计

    黑洞版视频裂变程序[接口版]全新上线,全新UI,支持分享数据统计!   后台效果   程序统一售价:1899/套(包安装,包更新) 注:本程序不属于之前视频程序的更新版,展现形式和广告位设置均不同,是 ...

  4. [转]玩转图片Base64编码

    转自:[前端攻略]:玩转图片Base64编码 图片处理在前端工作中可谓占据了很重要的一壁江山.而图片的 base64 编码可能相对一些人而言比较陌生,本文不是从纯技术的角度去讨论图片的 base64 ...

  5. TypeScript专题-Static和使用技巧

    class People { static _name: string; print() { //alert(this.name);// 编译不通过,doex not exist on type Pe ...

  6. 再回首数据结构—数组(Golang实现)

    数组为线性数据结构,通常编程语言都有自带了数组数据类型结构,数组存放的是有个相同数据类型的数据集: 为什么称数组为线性数据结构:因为数组在内存中是连续存储的数据结构,数组中每个元素最多只有左右两个方向 ...

  7. EXCLE 导入 或 导出

    首先要引用 NPOI.dll   (可在网上下载!)//导入public void OnSubmit()        {            string path = Server.MapPat ...

  8. for循环:用turtle画一颗五角星

    import turtle # 设置初始位置 turtle.penup() turtle.left(90) turtle.fd(200) turtle.pendown() turtle.right(9 ...

  9. Winform 窗体获得焦点

    给窗体添加Shown事件 public void Form_Shown(object sender, EventArgs e) { this.Activate(); this.Focus(); //定 ...

  10. 配置NFS固定端口

    NFS启动时会随机启动多个端口并向RPC注册,为了设置安全组以及iptables规则,需要设置NFS固定端口.NFS服务需要开启 mountd,nfs,nlockmgr,portmapper,rquo ...