文章目录
- AVL树
- AVL树的概念
- AVL树节点的定义
- AVL树的插入
- AVL树的旋转
- 右单旋
- 左单旋
- 左右双旋
- 右左双旋
- 代码实现
- 总结
AVL树
AVL树的概念
二叉搜索树在顺序有序或接近有序的情况下,而插入搜索树将退化为单叉树,此时查找的时间复杂度为O(n),效率低下。
两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新节点后,保证每个节点的左右子树高度差的绝对值不超过1,即可降低树的高度,减少平均搜索长度。因此,AVL树也被叫做高度平衡二叉搜索树,插入,查找,删除在平均和最坏情况下的时间复杂度都是O( l o g 2 n log_2 n log2n)。
AVL树节点的定义
template<class K, class V>struct AVLTreeNode{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;pair<K, V> _kv;int _bf; //balance factor 平衡因子AVLTreeNode(const pair<K, V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}};
注意:实现AVL树平衡因子不是必须的,只不过有了平衡因子帮助我们更便捷地控制整棵树。
AVL树的插入
- 根据二叉搜索树的规则插入新节点
bool Insert(const pair<K, V> & kv){root为空,特殊处理if (_root == nullptr){_root = new Node(kv);return true;}Node* curr = _root;Node* parent = nullptr;while (curr){if (curr->_kv.first < kv.first){parent = curr;curr = curr->_right;}else if (curr->_kv.first > kv.first){parent = curr;curr = curr->_left;}else{return false;}}将新节点和其父亲节点链接起来Node* newnode = new Node(kv);if (parent->_kv.first < kv.first)parent->_right = newnode;elseparent->_left = newnode;newnode->_parent = parent;......}
- 不断向上更新平衡因子
- 当前平衡因子为0,说明插入之前平衡因子为1 / -1,插入之后不改变树的高度,不会影响其他祖先节点,此时更新结束。
- 当前平衡因子为1 / -1,说明插入之前平衡因子为0,插入之后当前节点地高度发生变化,会影响其他祖先节点,但是不违反规则,需要向上对祖先节点进行更新,直至当前节点为root。
- 当前平衡因子为 2 / -2,此时当前节点所在地子树违反了平衡规则,需要进行处理–>旋转。
while (parent)
{if (parent->_left == newnode){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 0)break;else if (parent->_bf == -1 || parent->_bf == 1){newnode = parent;parent = parent->_parent;}else if (parent->_bf == -2 || parent->_bf == 2){旋转处理}else{assert(false);}
}
AVL树的旋转
右单旋
void RotatoR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* ppnode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppnode->_left == parent)ppnode->_left = subL;elseppnode->_right = subL;subL->_parent = ppnode;}parent->_bf = 0;subL->_bf = 0;
}
左单旋
void RotatoL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* ppnode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (ppnode->_left == parent)ppnode->_left = subR;elseppnode->_right = subR;subR->_parent = ppnode;}parent->_bf = 0;subR->_bf = 0;
}
左右双旋
旋转之前,45的平衡因子可能是-1/0/1,旋转完成之后,根据情况对其他节点的平衡因子进行调整
void RotatoLR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotatoL(subL);RotatoR(parent);subLR->_bf = 0;if (bf == 0){subL->_bf = 0;parent->_bf = 0;}else if (bf == 1){subL->_bf = -1;parent->_bf = 0;}else if (bf == -1){subL->_bf = 0;parent->_bf = 1;}
}
右左双旋
void RotatoRL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;subRL->_bf = 0;RotatoR(subR);RotatoL(parent);if (bf == 0){subR->_bf = 0;parent->_bf = 0;}else if (bf == 1){subR->_bf = 0;parent->_bf = -1;}else if (bf == -1){subR->_bf = 1;parent->_bf = 0;}
}
代码实现
namespace xxx
{template<class K, class V>struct AVLTreeNode{AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;pair<K, V> _kv;int _bf; //balance factor 平衡因子AVLTreeNode(const pair<K, V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}};template<class K, class V>class AVLTree{typedef AVLTreeNode<K, V> Node;public:bool Insert(const pair<K, V> & kv){if (_root == nullptr){_root = new Node(kv);return true;}Node* curr = _root;Node* parent = nullptr;while (curr){if (curr->_kv.first < kv.first){parent = curr;curr = curr->_right;}else if (curr->_kv.first > kv.first){parent = curr;curr = curr->_left;}else{return false;}}Node* newnode = new Node(kv);if (parent->_kv.first < kv.first)parent->_right = newnode;elseparent->_left = newnode;newnode->_parent = parent;while (parent){if (parent->_left == newnode){parent->_bf--;}else{parent->_bf++;}if (parent->_bf == 0)break;else if (parent->_bf == -1 || parent->_bf == 1){newnode = parent;parent = parent->_parent;}else if (parent->_bf == -2 || parent->_bf == 2){if (parent->_bf == 2 && newnode->_bf == 1){RotatoL(parent);}else if (parent->_bf == -2 && newnode->_bf == -1){RotatoR(parent);}else if (parent->_bf == -2 && newnode->_bf == 1){RotatoLR(parent);}else if (parent->_bf == 2 && newnode->_bf == -1){RotatoRL(parent);}else{assert(false);}break;}else{assert(false);}}return true;}void RotatoL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* ppnode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{if (ppnode->_left == parent)ppnode->_left = subR;elseppnode->_right = subR;subR->_parent = ppnode;}parent->_bf = 0;subR->_bf = 0;}void RotatoR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;subL->_right = parent;Node* ppnode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppnode->_left == parent)ppnode->_left = subL;elseppnode->_right = subL;subL->_parent = ppnode;}parent->_bf = 0;subL->_bf = 0;}void RotatoLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotatoL(subL);RotatoR(parent);subLR->_bf = 0;if (bf == 0){subL->_bf = 0;parent->_bf = 0;}else if (bf == 1){subL->_bf = -1;parent->_bf = 0;}else if (bf == -1){subL->_bf = 0;parent->_bf = 1;}}void RotatoRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;subRL->_bf = 0;RotatoR(subR);RotatoL(parent);if (bf == 0){subR->_bf = 0;parent->_bf = 0;}else if (bf == 1){subR->_bf = 0;parent->_bf = -1;}else if (bf == -1){subR->_bf = 1;parent->_bf = 0;}}void InOrder(){_InOrder(_root);cout << endl;}bool IsAVLTree(){return _IsAVLTree(_root);}private:bool _IsAVLTree(Node* root){if (root == nullptr)return true;int leftH = Height(root->_left);int rightH = Height(root->_right);return abs(leftH - rightH) <= 1&& _IsAVLTree(root->_left)&& _IsAVLTree(root->_right);}int Height(Node* node){if (node == nullptr)return 0;int leftH = Height(node->_left);int rightH = Height(node->_right);return 1 + max(leftH, rightH);}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_kv.second << ' ';_InOrder(root->_right);}Node* _root = nullptr;};
}
总结
AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即 l o g 2 ( N ) log_2 (N) log2(N)。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。