AVL树AVL树概念如果是以有序或接近有序的顺序建二叉搜索树,会导致二叉搜索树退化单枝树,相当于在顺序表中查找,效率低下。
因此,引入了
AVL树**(当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整) )**,使二叉树左右均衡,具有较高的查找效率。
AVL树可以是空树,或者是具有以下性质的二叉搜索树:
它的左右子树都是
AVL树左右子树高度之差(平衡因子)的绝对值不超过1(-1/0/1)
如果一颗二叉搜索树是平衡的,就是
AVL树,如果有n个结点,其高度可保持在log n,时间复杂度在O(log n)
AVL树节点的定义template<class K,class V> struct AVLTree_Node { pair<K, V> _data; //该节点存储的数据 AVLTree_Node<K,V>* _left; //该节点的左孩子 AVLTree_Node<K,V>* _right; //该节点的右孩子 AVLTree_Node<K,V>* _parent; //该节点的双亲节点 int _rf; //该节点的平衡因子(右子树的高度-左子树的高度) AVLTree_Node(const pair<K,V>& data) //构造函数 :_data(data) ,_left(nullptr) ,_right(nullptr) ,_parent(nullptr) ,_rf(0) {} };
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
AVL树的插入
AVL树在普通二叉搜索树的基础上引入了平衡因子,因此AVL树也是二叉搜索树,那么AVL树的插入过程分为两步:
按照二叉搜索树的规则插入新节点
调整节点的平衡因子
bool Insert(const pair<K,V>& x) { if (_root == nullptr) { _root = new Node(x); return true; } Node* cur = _root; Node* parent = nullptr; while (cur) { if (cur->_data.first < x.first) { parent = cur; cur = cur->_right; } else if (cur->_data.first > x.first) { parent = cur; cur = cur->_left; } else { return false; //AVL树会去重,如果在AVL树已存在与新插入节点K相等的节点,插入失败 } } cur = new Node(x); if (parent->_data.first < x.first) { parent->_right = cur; } else { parent->_left = cur; } cur->_parent = parent; /cur是新插入的节点,parent是cur的双亲节点, /在插入之后,cur的平衡因子一定为0,parent的平衡因子有五种可能,可能为0,-1,1,-2,2 /在插入之前,parent的平衡因子可能是0/-1/1(因为在插入一个新节点前,这已经是一个AVL树) /在插入之后,parent的平衡因子可能为0(之前可能为-1/1),-1(之前为0),1(之前为 0),-2(之前为-1),2(之前为1) /如果插入后平衡因子为0,那么以parent为根节点的子树高度没有改变,那就不会影响parent以上的 节点的平衡因子 /如果插入后平衡因子为1/-1,那么以parent为根节点的子树高度发生了改变,就会影响parent以上的 节点的平衡因子,就需要一层一层向上更新,直到某个节点的平衡因子更新为0/-2/2 /如果插入后平衡因子为-2/2,那么就需要对parent为根节点的子树进行处理 while (parent) //循环是为了向上更新 { if (parent->_right == cur) //更新parent的平衡因子 { parent->_rf++; } else { parent->_rf--; } if (parent->_rf == 0) //更新后,parent的平衡因子为0,插入结束 (1、不需要向上更新,满足AVL树规则) { break; } else if (parent->_rf == -1 || parent->_rf == 1) /更新后,parent的平衡因子为-1/1,需要向上更新 { cur = cur->_parent; parent = parent->_parent; } else if (parent->_rf == -2 || parent->_rf == 2) /更新后,parent的平衡因子为-2/2,违反AVL树规则,需要处理(旋转),旋转之后parent的 平衡因子为0(高度没有变化,不影响parent以上的节点),平衡因子符合规则,插入结束 /如果插入之前是h+2,插入节点,旋转之后,高度还是h+2 { if (parent->_rf == 2 && cur->_rf == 1)//左单旋 { RotateL(parent); } else if (parent->_rf == 2 && cur->_rf == -1)//cur右旋,parent左旋 { RotateRL(parent); } else if (parent->_rf == -2 && cur->_rf == -1)//右单旋 { RotateR(parent); } else if (parent->_rf == 2 && cur->_rf == 1)//cur左旋,parent右旋 { RotateLR(parent); } break; } else assert(false); } return true; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
AVL树的旋转
AVL树公有四种旋转(如果节点插入之前树的高度是h+2,插入,旋转之后,高度还是h+2)
右单旋
在30节点的左子树插入新节点,左边高,60树右旋
void RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; Node* ppnode = parent->_parent; parent->_left = subLR; if (subLR) { subLR->_parent = parent; } subL->_right = parent; parent->_parent = subL; if (_root == parent) { _root = subL; _root -> _parent = nullptr; } else { if (ppnode->_left == parent) { ppnode->_left = subL; } else { ppnode->_right = subL; } subL->_parent = ppnode; } subL->_rf = parent->_rf = 0; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
左单旋
在60节点右子树插入一个节点,右边高,30树左旋
void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; Node* ppnode = parent->_parent; parent->_right = subRL; if (subRL) { subRL->_parent = parent; } subR->_left = parent; parent->_parent = subR; if (_root == parent) { _root = subR; _root->_parent = nullptr; } else { if (ppnode->_left == parent) { ppnode->_left = subR; } else { ppnode->_right = subR; } subR->_parent = ppnode; } subR->_rf = parent->_rf = 0; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
左右单旋(h为0时,60节点是新插入的)
在30节点的右子树插入节点,90树的平衡因子违反规则,对于30节点右边高,对于90的节点左边高,所以需要先将30子树左旋,再将90树右旋
void RotateLR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; RotateL(subL); RotateR(parent); if (subLR->_rf == 1) { subL->_rf = -1; subLR->_rf = 0; parent->_rf = 0; } else if (subLR->_rf == -1) { subL->_rf = 0; parent->_rf = 1; subLR->_rf = 0; } else if (subLR->_rf == 0) //h为0,subLR为插入结点 { subL->_rf = 0; parent->_rf = 0; subLR->_rf = 0; } else { assert(false); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
右左单旋(h为0时,60是新插入的)
在90节点左子树插入新节点,30树的平衡因子违反规则,对于90节点左边高,对于30节点右边高,所以先把90树右旋,再把30树左旋
void RotateRL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; RotateR(subR); RotateL(parent); if (subRL->_rf == 1) { subR->_rf = 0; subRL->_rf = 0; parent->_rf = -1; } else if (subRL->_rf == -1) { subR->_rf = 1; parent->_rf = 0; subRL->_rf = 0; } else if (subRL->_rf == 0) { subR->_rf = 0; parent->_rf = 0; subRL->_rf = 0; } else { assert(false); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
为什么抽象图是这么画的?
因为只有这样画,才有可能使当前整个数是需要处理的
AVL树的验证
AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:
验证其为二叉搜索树
如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
验证其为平衡树
- 每个节点子树高度差的绝对值不超过1
- 节点的平衡因子是否计算正确
bool _IsBalanceTree(Node* root) { // 空树也是AVL树 if (nullptr == root) return true; // 计算pRoot节点的平衡因子:即pRoot左右子树的高度差 int leftHeight = _Height(root->_left); //_Height计算高度 int rightHeight = _Height(root->_right); int diff = rightHeight - leftHeight; // 如果计算出的平衡因子与pRoot的平衡因子不相等,或者 // pRoot平衡因子的绝对值超过1,则一定不是AVL树 if (abs(diff) >= 2) { cout << root->_data.first << "节点平衡因子异常" << endl; return false; } if (diff != root->_rf) { cout << root->_data.first << "节点平衡因子不符合实际" << endl; return false; } // pRoot的左和右如果都是AVL树,则该树一定是AVL树 return _IsBalanceTree(root->_left)&& _IsBalanceTree(root->_right); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
AVL树的性能
AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即O(log n)。但是如果要对AVL树做一些结构修改的操作,性能非常低下。