浅析二叉平衡树之AVL树

在前面的文章中介绍了二叉搜索树,尽管它可以缩短查找的速率,但是在数据有序或者接近有序的情况下,二叉搜索树就会退化成一棵单支树,查找的效率就会变得低下。因此,就有两位伟大的数学家发明了一种新的数据结构——AVL树,解决了二叉搜索树的一些不足。

什么是AVL树?(概念)

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
1、它的左右子树都是AVL树
2、左子树和右子树高度之差(简称平衡因子)的绝对值不超过1(-1、0、1)

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在O(lgn),平均搜索时间复杂度O(lg(n))。

如图:

《浅析二叉平衡树之AVL树》《浅析二叉平衡树之AVL树》《浅析二叉平衡树之AVL树》

AVL树的插入:

①若是空树,则直接插入;

②若不是空树,寻找插入位置,若树中有相同的数据则不进行插入,直接返回;

③插入成功后要检查平衡因子,必须要满足平衡因子的绝对值小于2;

④更新平衡因子,对树进行调整;

在根据平衡因子调整树的情况分为一下几种:

假设新插入的节点为p(新插入的节点的平衡因子为0),其双亲节点为pr

情况一:结点Pr的平衡因子为0
在Pr的较矮的子树上插入新节点,结点Pr平衡,其高度没有增加,此时从Pr到根路径上各结点为根的子树的高度不变,即各结点的平衡因子不变,结束平衡化处理。

情况二:结点Pr的平衡因子的绝对值为1;
插入前Pr的平衡因子为0,插入后以Pr为根的子树没有失去平衡,但该子树的高度增加,需从该结点Pr向根节点方向回溯,继续查看Pr的双亲结点的平衡性。

情况三:结点Pr的平衡因子的绝对值为2(说明经过了情况二的回溯)新节点在较高的子树插入,需要做平衡化处理:
若Pr = 2,说明右子树高,设Pr的右子树为q;
当q的平衡因子为1,执行左单旋转;
当q的平衡因子为-1,执行先右后左双旋转;
若Pr = -2,说明左子树高,设Pr的左子树为q;
当q的平衡因子为-1,执行右单旋转;
当q的平衡因子为1,执行先左后右双旋转;
旋转后Pr为根的子树高度降低,无需继续向上层回溯;


接下来我们主要看这几种旋转的情况:

左单旋:

《浅析二叉平衡树之AVL树》

        //左单旋 void _RotatelL(Node* parent) { assert(parent); Node* SubR = parent->_right; Node* SubRL = SubR->_left; Node* pparent = parent->_parent; parent->_right = SubRL; if (SubRL)//判断SubRL是否为空 SubRL->_parent = parent; SubR->_left = parent; parent->_parent = SubR; //判断parent是否为根节点 if (pparent == NULL)//parent是根节点 { _root = SubR; _root->_parent = NULL; } else//parent不是根节点 { if (parent == pparent->_left) { pparent->_left = SubR; } else { pparent->_right = SubR; } SubR->_parent = pparent; } parent->bf = SubR->bf = 0; }


右单旋:

《浅析二叉平衡树之AVL树》

//右单旋 void _RotatelR(Node* parent) { assert(parent); Node* SubL = parent->_left; Node* SubLR = SubL->_right; Node* pparent = parent->_parent; parent->_left = SubLR; if (SubLR) SubLR->_parent = parent; SubL->_right = parent; parent->_parent = SubL; if (pparent == NULL)//如果parent是根节点 { _root = SubL; _root->_parent = NULL; } else//不是根节点 { if (parent == pparent->_left) pparent->_left = SubL; else pparent->_right = SubL; SubL->_parent = pparent; } parent->bf = SubL->bf = 0; }

左右双旋

《浅析二叉平衡树之AVL树》

左右双旋后,平衡因子有以下几种情况;

情况一:SubLR即为插入节点

《浅析二叉平衡树之AVL树》《浅析二叉平衡树之AVL树》《浅析二叉平衡树之AVL树》《浅析二叉平衡树之AVL树》

情况二:插入的节点为SubLR的右节点

《浅析二叉平衡树之AVL树》《浅析二叉平衡树之AVL树》《浅析二叉平衡树之AVL树》《浅析二叉平衡树之AVL树》

情况三:插入的节点为SubLR的左节点

《浅析二叉平衡树之AVL树》

//左右双旋 void _RotatelLR(Node* parent) { assert(parent); Node* SubL = parent->_left; Node* SubLR = SubL->_right; int bf = SubLR->bf; _RotatelL(SubL);//先左旋 _RotatelR(parent);//再右旋 if (bf == 0)//说明SubLR为新插入的节点 parent->bf = SubL->bf = 0; else if (bf == 1)//说明插入的节点在SubLR的右边 { SubL->bf = -1; parent->bf = 0; } else//说明插入的节点在SubLR的左边 { SubL->bf = 0; parent->bf = 1; } SubLR->bf = 0;//最后,SubLR的孩子节点即左孩子或右孩子都分走了,因此要将SubLR的bf置0 }

左右双旋和单旋不同的是,平衡因子变化会与插入的节点位置有关即与SubLR的平衡因子有关,分为三种情况:①SubLR为插入点;②在SubLR的右边插入;③在SubLR的左边插入;这三种情况对应的SubLR的平衡因子有三种0、1和-1;因此对应parent和SubL的平衡因子就有三种情况:

《浅析二叉平衡树之AVL树》

右左双旋和左右双旋类似,这里就不多阐述。

以下是全部的源码:

#include<iostream> #include<cassert> using namespace std; template<class T, class V> struct AVLTreeNode { AVLTreeNode<T, V>* _left; AVLTreeNode<T, V>* _right; AVLTreeNode<T, V>* _parent; T _key; V _value; int bf;//平衡因子 AVLTreeNode(const T& key, const V& value) :_left(NULL) , _right(NULL) , _parent(NULL) , _key(key) , _value(value) , bf(0) {} }; template<class T, class V> class AVLTree { typedef AVLTreeNode<T, V> Node; public: AVLTree() :_root(NULL) {} ~AVLTree() { Destory(_root); } //插入 bool Insert(const T& key, const V& value) { //如果根节点,直接插入 if (_root == NULL) { _root = new Node(key,value); return true; } //不是根节点 Node* cur = _root; Node* parent = NULL; //寻找插入位置 while (cur) { if (key < cur->_key)//向左 { parent = cur; cur = cur->_left; } else if (key > cur->_key)//向右 { parent = cur; cur = cur->_right; } else//树里已经有key,直接返回 { return false; } } //找到了要插入的位置 cur = new Node(key,value); //判断要插入的位置在双亲节点的左/右 if (key < parent->_key) { parent->_left = cur; cur->_parent = parent; } else { parent->_right = cur; cur->_parent = parent; } //插入后需要更新平衡因子 while (parent) { //先更新双亲节点的平衡因子 if (parent->_left == cur) parent->bf--; else parent->bf++; //更新完后检查双亲的平衡因子是否平衡 if (parent->bf == 0)//平衡因子为0则平衡 return true; else if (parent->bf == 1 || parent->bf == -1)//平衡因子绝对值为1, { //则要判断parent的双亲节点的平衡因子,进行向上回溯 cur = parent; parent = parent->_parent; } else if (parent->bf == 2 || parent->bf == -2)//不平衡则进行调整 { //分两种情况 //第一种 if (parent->bf == 2) { if (cur->bf == 1)//判断是否同号 { _RotatelL(parent);//左旋 //return true; } else//cur->bf == -1 异号 { _RotatelRL(parent); //return true; } } //第二种 else if (parent->bf == -2) { if (cur->bf == -1)//同号 { _RotatelR(parent); //return true; } else // 异号 cur->bf == 1 { _RotatelLR(parent); //return true; } } break; } } return true; } bool Find(const K& key) { Node* cur = _root; while (cur) { if (key < cur->_key) cur = cur->_left; else if (key > cur->_key) cur = cur->_right; else return true; } return false; } Node* Destory(Node* root) { if (root == NULL) return NULL; else { Destory(root->_left); Destory(root->_right); delete root; root = NULL; } return root; } void Inorder() { _InOrder(_root); cout << endl; } //判断平衡 bool Isbalance() { int hight = 0; return _Isbalance(_root, hight); } private: //左单旋 void _RotatelL(Node* parent) { assert(parent); Node* SubR = parent->_right; Node* SubRL = SubR->_left; Node* pparent = parent->_parent; parent->_right = SubRL; if (SubRL)//判断SubRL是否为空 SubRL->_parent = parent; SubR->_left = parent; parent->_parent = SubR; //判断parent是否为根节点 if (pparent == NULL)//parent是根节点 { _root = SubR; _root->_parent = NULL; } else//parent不是根节点 { if (parent == pparent->_left) { pparent->_left = SubR; } else { pparent->_right = SubR; } SubR->_parent = pparent; } parent->bf = SubR->bf = 0; } //右单旋 void _RotatelR(Node* parent) { assert(parent); Node* SubL = parent->_left; Node* SubLR = SubL->_right; Node* pparent = parent->_parent; parent->_left = SubLR; if (SubLR) SubLR->_parent = parent; SubL->_right = parent; parent->_parent = SubL; if (pparent == NULL)//如果parent是根节点 { _root = SubL; _root->_parent = NULL; } else//不是根节点 { if (parent == pparent->_left) pparent->_left = SubL; else pparent->_right = SubL; SubL->_parent = pparent; } parent->bf = SubL->bf = 0; } //左右双旋 void _RotatelLR(Node* parent) { assert(parent); Node* SubL = parent->_left; Node* SubLR = SubL->_right; int bf = SubLR->bf; _RotatelL(SubL);//先左旋 _RotatelR(parent);//再右旋 if (bf == 0)//说明SubLR为新插入的节点 parent->bf = SubL->bf = 0; else if (bf == 1)//说明插入的节点在SubLR的右边 { SubL->bf = -1; parent->bf = 0; } else//说明插入的节点在SubLR的左边 { SubL->bf = 0; parent->bf = 1; } SubLR->bf = 0;//最后,SubLR的孩子节点即左孩子或右孩子都分走了,因此要将SubLR的bf置0 } //右左双旋 void _RotatelRL(Node* parent) { assert(parent); Node* SubR = parent->_right; Node* SubRL = SubR->_left; int bf = SubRL->bf; _RotatelR(SubR); _RotatelL(parent); if (bf == 0)//说明插入的节点是SubRL parent->bf = SubR->bf = 0; else if (bf == 1)//说明插入的节点在SubR的右边 { SubR->bf = 0; parent->bf = -1; } else//说明插入的节点在SubR的左边 { SubR->bf = 1; parent->bf = 0; } SubRL->bf = 0; //最后,SubRL的孩子节点即左孩子或右孩子都分走了,因此要将SubRL的bf置0 } //中序遍历 void _InOrder(Node* root) { if (root) { _InOrder(root->_left); cout << root->_key << " "; _InOrder(root->_right); } } //求树的高度 size_t _height(Node* root) { if (root == NULL) return 0; size_t hightL = _height(root->_left)+1; size_t hightR = _height(root->_right) + 1; return hightL > hightR ? hightL : hightR; } //判断平衡 bool _Isbalance(Node* root, int& hight) { if (root == NULL) { hight = 0; return true; } int hightL = 0; int hightR = 0; if (_Isbalance(root->_left, hightL) == false) return false; if (_Isbalance(root->_right, hightR) == false) return false; if (hightR - hightL != root->bf) { cout << "平衡因子异常" <<root->_key<< endl; } hight = hightL > hightR ? hightL+1 : hightR+1; return abs(hightL - hightR) < 2; } bool _Isbalance2(Node* root) { if (root == NULL) return true; int hightL = _Isbalance2(root->_left); int hightR = _Isbalance2(root->_right); return abs(hightL - hightR) < 2 && _Isbalance2(root->_left) && _Isbalance2(root->_right); } private: Node* _root; };
    原文作者:AVL树
    原文地址: https://blog.csdn.net/L__xing/article/details/80098881
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞