飞道的博客

C++---AVL树

427人阅读  评论(0)

目录

一、AVL树的概念

1、什么是AVL树?

2、AVL树的节点定义

二、AVL树的插入操作

1、插入节点

2、调整平衡因子

3、旋转成为一颗AVL树

三、AVL树的实现


一、AVL树的概念

1、什么是AVL树?

当数据有序或者接近有序时,使用二叉搜索树进行存储时,得到的二叉搜索树是一颗单支树,其搜索的时间复杂度为O(N)。

为了解决上述问题,引入了AVL树的概念:

  • AVL树是一颗特殊的二叉搜索树
  • 向AVL树中插入一个节点后,树的所有节点的左右孩子节点的高度差的绝对值小于等于1.
  • 即AVL树是一颗二叉搜索树是,它的左右子子树的高度之差(平衡因子)的绝对值不大于1,并且它的左右子树也是一颗AVL树。

2、AVL树的节点定义

template<class T>

struct TreeNode

{

        //构造函数

        TreeNode(const T& val)

                :_val(val),_bf(0),_left(nullptr),_right(nullptr),_parent(nullptr)

        {}

        T _val;

        TreeNode* _left;//左孩子

        TreeNode* _right;//右孩子

        TreeNode* _parent;//双亲节点,不是必须的,为了方便实现而引入的

        int _bf;//平衡因此,不是必须的,为了方便实现引入的。规定,左子树比右子树高平衡因子为-1,一样高为0,右高为1

};

二、AVL树的插入操作

AVL树也是一颗二叉搜索树,因此它在插入数据时也需要先找到要插入的位置然后在将节点插入。不同的是,AVL树插入节点后需要对节点的平衡因子进行调整,如果插入节点后平衡因子的绝对值大于1,则还需要对该树进行旋转,旋转成为一颗高度平衡的二叉搜索树。

1、插入节点

该步和二叉搜索树一样,先找到节点在进行插入。需要注意的是,如果是空树直接插入并且让树的根节点等于当前节点。


  
  1. //1.找到该节点并插入
  2. Node* newNode = new Node(val);
  3. //如果是一颗空树,新插入节点就为跟节点
  4. if(root == nullptr)
  5. {
  6. root = newNode;
  7. return make_pair(root, true);
  8. }
  9. //找到插入位置
  10. Node* parent = nullptr; //当前节点的父节点
  11. Node* cur = root;
  12. while(cur)
  13. {
  14. if((cur->_val).first < (newNode->_val).first)
  15. {
  16. //左走
  17. parent = cur;
  18. cur = cur->left;
  19. }
  20. else if((cur->_val).first > (newNode->_val).first)
  21. {
  22. //右走
  23. parent = cur;
  24. cur = cur->right;
  25. }
  26. else
  27. {
  28. //找到了,不需要插入,直接返回
  29. return make_pair(cur, false);
  30. }
  31. }
  32. //插入节点
  33. if((parent->_val).first > (newNode->_val).first)
  34. {
  35. //插入到parent的左孩子节点处
  36. parent->left = newNode;
  37. newNode->_parent = parent;
  38. return make_pair(newNode, true);
  39. }
  40. else
  41. {
  42. //插入到parent的右孩子节点处
  43. parent->right = newNode;
  44. newNode->_parent = parent;
  45. return make_pair(newNode, true); }

2、调整平衡因子

  • 插入一个一个节点,只会影响根节点到插入节点的父节点上的平衡因子。

  • 如果插入一个节点后,插入节点的父节点的平衡因子变成了0,则说明插入节点后树的高度没有发生变化,则只影响了父节点的平衡因子。

  • 如果插入一个节点后,该节点的父节点的平衡因子的绝对值大于等于1,在向上更新过程中如果某一个节点的平衡因子变成了0则停止更新,最坏情况下一直要更新到根节点。

代码实现:


  
  1. //2.调节平衡因子
  2. /*基本思路
  3. * 从该节点的父节点开始调整,如果父节点的平衡因子变成了0,则停止调整
  4. * 如果该节点的父节点的平衡因子不是0,则继续向上调整,直到某个节点的
  5. * 平衡因子变成0或者调整到了根节点
  6. * 调整平衡因子的策略是,如果插入的节点是该节点的左孩子节点则平衡因子-1
  7. * 如果是该节点的右孩子节点,则平衡因子+1
  8. */
  9. cur = newNode;
  10. while(parent)
  11. {
  12. if(cur == parent->left)
  13. {
  14. //parent的平衡因子-1
  15. parent->_bf--;
  16. }
  17. else
  18. {
  19. //parent的平衡因子+1
  20. parent->_bf++;
  21. }
  22. //判断parent的平衡因子是否为0
  23. if(parent->_bf == 0)
  24. {
  25. //调整完成
  26. break;
  27. }
  28. else
  29. {
  30. //继续调整parent的父节点
  31. parent = parent->_parent;
  32. } }

3、旋转成为一颗AVL树

向AVL树中插入一个节点后,节点的平衡因子可能会发生变化,因此需要对节点的平衡因子进行调整。但是,调整后的节点的平衡因子可能会大于1,也就是说插入一个节点后不在是一颗AVL树。因此,需要通过旋转将调整后的树旋转成一颗AVL树。

1)右单旋:新插入的节点是较高的左子树的左孩子节点

思考:如果新插入的节点是2的孩子节点,可以使用右单旋吗?

注意:要插入的节点必须是较高的左子树(对于-2来说,较高的左子树的根节点是1)的左孩子节点(对于1来说左孩子节点的根节点必须是0),因此当插入到2的时候不能使用右单旋。这时就需要使用左右单旋。

代码描述:


  
  1. //右单旋
  2. void RotateR(Node* parent)
  3. {
  4. Node* parentL = parent->_left; //parent的左孩节点
  5. Node* subR = parentL->_right; //parent的左孩子节点的右孩子节点
  6. Node* parentParent = parent->parent;
  7. //parent的左孩子节点指向parentL的右孩子节点
  8. parentL = subR;
  9. if(subR)
  10. subR->_parent = parent;
  11. //parent变成parentL的右孩子节点
  12. subR = parent;
  13. //parent的父节点变成parent的左孩子节点
  14. parent->_parent = parentL;
  15. //parent是根节点
  16. if(parent == root)
  17. {
  18. root = parentL;
  19. root->_parent = nullptr;
  20. }
  21. else
  22. {
  23. if(parent == parentParent->_left)
  24. {
  25. parentParent->_left = parentL;
  26. parentL->_parent = parentParent;
  27. }
  28. else
  29. {
  30. parentParent->_right = parentL;
  31. parentL->_parent = parentParent;
  32. }
  33. }
  34. parent->_bf = parentL->_bf = 0; }

2)左单旋:新插入节点是较高的右子树的右孩子节点

注意:如果新插入的节点是较高的右子树的左孩子节点时需要使用右左单旋

代码描述


  
  1. //左单旋
  2. void RotateL(Node* parent)
  3. {
  4. Node* parentR = parent->_right; //parent的右孩子节点
  5. Node* subRL = parentR->_left; //parent的右孩子的左孩子节点
  6. Node* parentParent = parent->_parent;
  7. //parent的右孩子节点指向parent的右孩子节点的左孩子节点
  8. parentR = subRL;
  9. if(subRL)
  10. subRL->_parent = parentR;
  11. //parent的右孩子节点的左孩子节点指向parent节点
  12. subRL = parent;
  13. //parent的父节点指向parent的右孩子节点
  14. parent->_parent = parentR;
  15. if(parent == root)
  16. {
  17. root = parentR;
  18. root->_parent = nullptr;
  19. }
  20. else
  21. {
  22. if(parentParent->_left == parent)
  23. {
  24. parentParent->_left = parent;
  25. }
  26. else
  27. {
  28. parentParent->_right = parent;
  29. }
  30. arent->_parent = parentParent;
  31. }
  32. parent->_bf = parentR->_bf = 0;
  33. }

3)左右单旋:新插入节点是较高的左子树的右孩子节点

注意:左右单旋是指先对该节点的左孩子节点进行左单旋,在对该节点进行右单旋

代码描述


  
  1. //左右单旋
  2. void RotateLR(Node* parent)
  3. {
  4. Node* parentL = parent->_left;
  5. Node* subLR = parentL->_right;
  6. int subLRBf = subLR->_bf;
  7. //左孩子节点进行左单旋
  8. RotateL(parent->_left);
  9. RotateR(parent);
  10. //调整平衡因子
  11. if(subLRBf == 1)
  12. {
  13. parent->_bf = subLR->_bf = 0;
  14. parentL->_bf = -1;
  15. }
  16. else if(subLRBf == -1)
  17. {
  18. subLR->_bf = parentL->_bf = 0;
  19. parent->_bf = -1;
  20. }
  21. else
  22. {
  23. parent->_bf = parentL->_bf = subLR->_bf = 0;
  24. }
  25. }

4)右左单旋:新插入节点在较高右子树的左孩子节点

代码描述


  
  1. //右左单旋
  2. void RotateRL(Node* parent)
  3. {
  4. Node* parentR = parent->_right;
  5. Node* subRL = parentR->_left;
  6. int subRLBf = subRL->_bf;
  7. //该节点的右孩子进行右旋转
  8. RotateR(parent->_right);
  9. //该节点进行左旋转
  10. RotateL(parent);
  11. //调整平衡因子
  12. if(subRLBf == 1)
  13. {
  14. parentR->_bf = subRL->_bf = 0;
  15. parent->_bf = -1;
  16. }
  17. else if(subRLBf == -1)
  18. {
  19. parentR->_bf = subRL->_bf = 0;
  20. parent->_bf = 1;
  21. }
  22. else
  23. {
  24. parentR->_bf = subRL->_bf = parent->_bf = 0;
  25. } }

5)验证

验证一颗二叉树是否是AVL树时,只要满足以下两个方面就说明该二叉树是AVL树:

  • 该二叉树是一颗二叉搜索树:中序遍历得到有序序列
  • 是否是平衡的:左右子树的平衡因子之差的绝对值小于等于1;插入节点、旋转之后平衡因子是否更新正确、

代码描述


  
  1. //中序遍历AVL树
  2. static void BSTreeInOrder(Node* node,vector<pair<K,V>>& inOrder)
  3. {
  4. //inOrder是输出型参数,将遍历结果保存到该数组中
  5. if(node == nullptr)
  6. return;
  7. BSTreeInOrder(node->_left,inOrder);
  8. inOrder.push_back(node->_val);
  9. BSTreeInOrder(node->_right,inOrder);
  10. }
  11. bool isBSTree()
  12. {
  13. vector< pair<K,V>> inOrder; BSTreeInOrder(root,inOrder);
  14. if(inOrder.empty())
  15. return true;
  16. //遍历,检查是否有序
  17. pair<K,V> tmp = inOrder[ 0];
  18. for( int i = 1;i < inOrder.size();i++)
  19. {
  20. if(tmp.first < inOrder[i].first)
  21. {
  22. tmp = inOrder[i];
  23. }
  24. else
  25. return false;
  26. }
  27. }
  28. //二叉树的高度
  29. static int BSTreeHeight(Node* node)
  30. {
  31. if(node == nullptr)
  32. return 0;
  33. int left = BSTreeHeight(node->_left);
  34. int right = BSTreeHeight(node->_right);
  35. return left>right?(left+ 1):(right+ 1);
  36. }
  37. //判断是否平衡
  38. static bool _isBalance(Node* root)
  39. {
  40. //求左右子树的高度
  41. int left = BSTreeHeight(root->_left);
  42. int right = BSTreeHeight(root->_right);
  43. if( abs(left-right) <= 1)
  44. {
  45. return _isBalance(root->_left) && _isBalance(root->_right); }
  46. else
  47. return false;
  48. }
  49. //验证AVL树
  50. bool isBalance()
  51. {
  52. if(root == nullptr)
  53. return true;
  54. if(isBSTree() && _isBalance())
  55. return true;
  56. return false;
  57. }

三、AVL树的实现


  
  1. #pragma once
  2. #include<iostream>
  3. #include<stdlib.h>
  4. #include<vector>
  5. using namespace std;
  6. //节点定义
  7. template< class K,class V>
  8. struct AVLTreeNode
  9. {
  10. pair<K,V> _val; //节点,键值对
  11. int _bf; //平衡因子
  12. AVLTreeNode<K,V>* _left; //左子树
  13. AVLTreeNode<K,V>* _right; //右子树
  14. AVLTreeNode<K,V>* _parent; //双亲节点
  15. AVLTreeNode( const pair<K,V>& val)
  16. :_val(val),_bf( 0)
  17. ,_left( nullptr),_right( nullptr),_parent( nullptr)
  18. {}
  19. };
  20. template< class K,class V>
  21. class AVLTree
  22. {
  23. typedef AVLTreeNode<K,V> Node;
  24. private:
  25. Node* root = nullptr; //根节点
  26. public:
  27. //构造函数
  28. //析构函数
  29. //插入--返回一个键值对,键值对的第一个值是插入节点的指针第二个值是bool值表示是否插入成功
  30. pair<Node*,bool> insert(const pair<K,V>& val)
  31. { //1.找到该节点并插入
  32. Node* newNode = new Node(val);
  33. //如果是一颗空树,新插入节点就为跟节点
  34. if(root == nullptr)
  35. {
  36. root = newNode;
  37. return make_pair(root, true);
  38. }
  39. //找到插入位置
  40. Node* parent = nullptr; //当前节点的父节点
  41. Node* cur = root;
  42. while(cur)
  43. {
  44. if((cur->_val).first < (newNode->_val).first)
  45. {
  46. //左走
  47. parent = cur;
  48. cur = cur->left;
  49. }
  50. else if((cur->_val).first > (newNode->_val).first)
  51. {
  52. //右走
  53. parent = cur;
  54. cur = cur->right;
  55. }
  56. else
  57. {
  58. //找到了,不需要插入,直接返回
  59. return make_pair(cur, false);
  60. }
  61. }
  62. //插入节点
  63. if((parent->_val).first > (newNode->_val).first)
  64. {
  65. //插入到parent的左孩子节点处 parent->right = newNode;
  66. newNode->_parent = parent;
  67. }
  68. //2.调整平衡因子
  69. /*基本思路
  70. * 从该节点的父节点开始调整,如果父节点的平衡因子变成了0,则停止调整
  71. * 如果该节点的父节点的平衡因子不是0,则继续向上调整,直到某个节点的
  72. * 平衡因子变成0或者调整到了根节点
  73. * 调整平衡因子的策略是,如果插入的节点是该节点的左孩子节点则平衡因子-1
  74. * 如果是该节点的右孩子节点,则平衡因子+1
  75. */
  76. cur = newNode;
  77. while(parent)
  78. {
  79. if(cur == parent->left)
  80. {
  81. //parent的平衡因子-1
  82. parent->_bf--;
  83. }
  84. else
  85. {
  86. //parent的平衡因子+1
  87. parent->_bf++;
  88. }
  89. //判断parent的平衡因子是否为0
  90. if(parent->_bf == 0)
  91. {
  92. //调整完成
  93. break;
  94. }
  95. else if(parent->bf == -1 || parent->bf == 1)
  96. { //继续向上调整
  97. cur = parent;
  98. parent = parent->_parent;
  99. }
  100. else
  101. {
  102. //已经不再是一颗AVL树,需要进行旋转
  103. if(parent->_bf == -2)
  104. {
  105. if(parent->_left->_bf == -1)
  106. {
  107. //新插入的节点是较高的左子树的左孩子节点---右单旋
  108. RotateR(parent);
  109. }
  110. else
  111. {
  112. //左右单旋
  113. RotateLR(parent);
  114. }
  115. }
  116. else if(parent->_bf == 2)
  117. {
  118. if(parent->_right->_bf == 1)
  119. {
  120. //左单旋
  121. RotateL(parent);
  122. }
  123. else
  124. {
  125. //右左单旋
  126. RotateRL(parent);
  127. }
  128. }
  129. }
  130. } return make_pair(cur, false);
  131. }
  132. //右单旋
  133. void RotateR(Node* parent)
  134. {
  135. Node* parentL = parent->_left; //parent的左孩节点
  136. Node* subR = parentL->_right; //parent的左孩子节点的右孩子节点
  137. Node* parentParent = parent->parent;
  138. //parent的左孩子节点指向parentL的右孩子节点
  139. parentL = subR;
  140. if(subR)
  141. subR->_parent = parent;
  142. //parent变成parentL的右孩子节点
  143. subR = parent;
  144. //parent的父节点变成parent的左孩子节点
  145. parent->_parent = parentL;
  146. //parent是根节点
  147. if(parent == root)
  148. {
  149. root = parentL;
  150. root->_parent = nullptr;
  151. }
  152. else
  153. {
  154. if(parent == parentParent->_left)
  155. {
  156. parentParent->_left = parentL;
  157. parentL->_parent = parentParent;
  158. }
  159. else
  160. {
  161. parentParent->_right = parentL;
  162. parentL->_parent = parentParent;
  163. } }
  164. parent->_bf = parentL->_bf = 0;
  165. }
  166. //左单旋
  167. void RotateL(Node* parent)
  168. {
  169. Node* parentR = parent->_right; //parent的右孩子节点
  170. Node* subRL = parentR->_left; //parent的右孩子的左孩子节点
  171. Node* parentParent = parent->_parent;
  172. //parent的右孩子节点指向parent的右孩子节点的左孩子节点
  173. parentR = subRL;
  174. if(subRL)
  175. subRL->_parent = parentR;
  176. //parent的右孩子节点的左孩子节点指向parent节点
  177. subRL = parent;
  178. //parent的父节点指向parent的右孩子节点
  179. parent->_parent = parentR;
  180. if(parent == root)
  181. {
  182. root = parentR;
  183. root->_parent = nullptr;
  184. }
  185. else
  186. {
  187. if(parentParent->_left == parent)
  188. {
  189. parentParent->_left = parent;
  190. }
  191. else
  192. {
  193. parentParent->_right = parent;
  194. } parent->_parent = parentParent;
  195. }
  196. parent->_bf = parentR->_bf = 0;
  197. }
  198. //左右单旋
  199. void RotateLR(Node* parent)
  200. {
  201. Node* parentL = parent->_left;
  202. Node* subLR = parentL->_right;
  203. int subLRBf = subLR->_bf;
  204. //左孩子节点进行左单旋
  205. RotateL(parent->_left);
  206. RotateR(parent);
  207. //调整平衡因子
  208. if(subLRBf == 1)
  209. {
  210. parent->_bf = subLR->_bf = 0;
  211. parentL->_bf = -1;
  212. }
  213. else if(subLRBf == -1)
  214. {
  215. subLR->_bf = parentL->_bf = 0;
  216. parent->_bf = -1;
  217. }
  218. else
  219. {
  220. parent->_bf = parentL->_bf = subLR->_bf = 0;
  221. }
  222. }
  223. //右左单旋
  224. void RotateRL(Node* parent)
  225. {
  226. Node* parentR = parent->_right;
  227. Node* subRL = parentR->_left;
  228. int subRLBf = subRL->_bf;
  229. //该节点的右孩子进行右旋转
  230. RotateR(parent->_right);
  231. //该节点进行左旋转
  232. RotateL(parent);
  233. //调整平衡因子
  234. if(subRLBf == 1)
  235. {
  236. parentR->_bf = subRL->_bf = 0;
  237. parent->_bf = -1;
  238. }
  239. else if(subRLBf == -1)
  240. {
  241. parentR->_bf = subRL->_bf = 0;
  242. parent->_bf = 1;
  243. }
  244. else
  245. {
  246. parentR->_bf = subRL->_bf = parent->_bf = 0;
  247. }
  248. }
  249. //中序遍历AVL树
  250. static void BSTreeInOrder(Node* node,vector<pair<K,V>>& inOrder)
  251. {
  252. //inOrder是输出型参数,将遍历结果保存到该数组中
  253. if(node == nullptr)
  254. return;
  255. BSTreeInOrder(node->_left,inOrder);
  256. inOrder.push_back(node->_val);
  257. BSTreeInOrder(node->_right,inOrder);
  258. }
  259. bool isBSTree()
  260. {
  261. vector< pair<K,V>> inOrder; BSTreeInOrder(root,inOrder);
  262. if(inOrder.empty())
  263. return true;
  264. //遍历,检查是否有序
  265. pair<K,V> tmp = inOrder[ 0];
  266. for( int i = 1;i < inOrder.size();i++)
  267. {
  268. if(tmp.first < inOrder[i].first)
  269. {
  270. tmp = inOrder[i];
  271. }
  272. else
  273. return false;
  274. }
  275. }
  276. //二叉树的高度
  277. static int BSTreeHeight(Node* node)
  278. {
  279. if(node == nullptr)
  280. return 0;
  281. int left = BSTreeHeight(node->_left);
  282. int right = BSTreeHeight(node->_right);
  283. return left>right?(left+ 1):(right+ 1);
  284. }
  285. //判断是否平衡
  286. static bool _isBalance(Node* root)
  287. {
  288. //求左右子树的高度
  289. int left = BSTreeHeight(root->_left);
  290. int right = BSTreeHeight(root->_right);
  291. if( abs(left-right) <= 1)
  292. {
  293. return _isBalance(root->_left) && _isBalance(root->_right); }
  294. else
  295. return false;
  296. }
  297. //验证AVL树
  298. bool isBalance()
  299. {
  300. if(root == nullptr)
  301. return true;
  302. if(isBSTree() && _isBalance())
  303. return true;
  304. return false;
  305. }
  306. }

 


转载:https://blog.csdn.net/qq_47406941/article/details/117255744
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场