AVL树
简介:
- 当搜索二叉树退化为单支树时,搜索效率极低,为了使搜索效率高,建立平衡搜索二叉树就需要AVLTree这样的平衡树来解决。
- 如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。
- 根据节点插入位置的不同,AVL树的旋转分为四种:
1. 新节点插入较高左子树的左侧 --- 左左:右单旋即: 对60 进行右单旋2. 新节点插入较高右子树的右侧 --- 右右:左单旋即: 对30 进行左单旋3. 新节点插入较高左子树的右侧 --- 左右:先左单旋再右单旋将双旋变成单旋后再旋转,即: 先对 30 进行左单旋,然后再对 90 进行右单旋 ,旋转完成后再考虑平衡因子的更新。4. 新节点插入较高右子树的左侧 --- 右左:先右单旋再左单旋将双旋变成单旋后再旋转,即: 先对90 进行右单旋,然后再对30 进行左单旋 ,旋转完成后再考虑平衡因子的更新。总结:假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR当pSubR的平衡因子为1时,执行左单旋当pSubR的平衡因子为-1时,执行右左双旋2. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL当pSubL的平衡因子为-1是,执行右单旋当pSubL的平衡因子为1时,执行左右双旋旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新。5.代码实现:#define _CRT_SECURE_NO_WARNINGS 1 #include <iostream> #include <string> #include <assert.h> using namespace std; //二叉搜索树BinarySearchTree //struct BinarySearchTreeNode template<class K,class V> struct AVLTreeNode { AVLTreeNode<K,V>* _left;//一定不要写成BSTreeNode*<K> _left; 这样编译器无法识别 AVLTreeNode<K,V>* _right; AVLTreeNode<K,V>* _parent; pair<K, V> _kv; //右子树-左子树的高度差 int _bf; // balance factor 平衡因子 // AVL树并没有规定必须要设计平衡因子 只是一个实现的选择,方便控制平衡 AVLTreeNode(const pair<K, V>& kv) :_left(nullptr) ,_right(nullptr) ,_parent(nullptr) ,_kv(kv) ,_bf(0) {} }; template<class K, class V> class AVLTree { typedef struct AVLTreeNode<K, V> Node; public: bool insert(const pair<K, V>& kv) { if (_root == nullptr) { _root = new Node(kv); _root->_bf = 0; return true; } Node* parent = nullptr; Node* cur = _root; while (cur) { if (cur->_kv.first < kv.first) { parent = cur; cur = cur->_right; } else if (cur->_kv.first > kv.first) { parent = cur; cur = cur->_left; } else { return false; } } //准备从parent插入 cur = new Node(kv); if (parent->_kv.first > kv.first)//插左子树 { parent->_left = cur; } else//插右子树 { parent->_right = cur; } cur->_parent = parent;//新插入的节点指向parent 与父亲相连 (之所以在树节点中设置_parent是为了更新平衡因子方便,可以更快捷的搜索到父亲节点) //更新平衡因子 while (parent)// 不要疏忽写成while (cur); 最远可能更新到 根的平衡因子 { if (cur==parent->_left) { parent->_bf--; } else { parent->_bf++; } // -1 0 | 1 0 // / \ / \ | / \ / \ // 0 插入右边 0 0 | 0 插入左边 0 0 if (parent->_bf == 0) //插入节点后填上矮的那边, parent_bf: 由-1 or 1 -->> 0 高度不变,更新结束 { break; } //插入节点后: // 若parent_bf: 由0 -->> -1 or 1高度改变,继续更新 // 若parent_bf:由-1 or 1 -->> -2 or 2 子树出现不平衡,需要旋转处理(左单旋/右单旋/左右双旋/右左双旋) else if (parent->_bf == 1 || parent->_bf == -1) //插入节点后导致一边变高了,parent_bf: 由0 -->> -1 or 1 高度改变,继续更新parent_bf (子树的高度变了,继续更新祖先) { // 0 0 | 0 0 // / \ / \ | / \ / \ // 0 0 插入右边 0 1 | 0 0 插入左边 -1 0 // \ \ | / / // 0 | 0 cur = cur->_parent; parent = parent->_parent; // 0 1 | 0 -1 // / \ / \ | / \ / \ // 0 1 更新parent_bf 0 1 | -1 0 更新parent_bf -1 0 //直到更新到根节点的祖先时才结束 (_root->_parent==nullptr 进不到while(cur){}循环里,所以结束) // \ \ | / / // 0 0 | 0 0 } else if (parent->_bf == 2 || parent->_bf == -2)//插入节点后导致本来高一边又变高了,出现了parent_bf=±2,parent_bf: 由-1 or 1 -->> -2 or 2 子树出现不平衡,需要旋转处理(左单旋/右单旋/左右双旋/右左双旋) { //直线插入:单旋 折线插入:双旋 //直线插入:单旋 // a(1) a(2) b(0) | a(-1) a(-2) b(0) // / \ / \ / \ | / \ / \ / \ // b(0) b(1) a左单旋,更新平衡因子 a(0) c(0) | b(0) b(-1) a右单旋,更新平衡因子 c(0) a(0) // \ \ \ | / / / // c(0) | c(0) if (parent->_bf == 2 && cur->_bf == 1) // 左单旋 { RotateL(parent);//parent左单旋,更新平衡因子 } else if (parent->_bf == -2 && cur->_bf == -1) // 右单旋 { RotateR(parent);//parent右单旋,更新平衡因子 } //折线插入:双旋 //①c为新增节点(左右双旋和右左双旋) // a(-1) a(-2) a(-2) c(0) | a(1) a(2) a(2) c(0) // / \ / \ / \ / \ | / \ / \ / \ / \ // b(0) b(1) b先左单旋 c(-1) a再右单旋 b(0) a(0) 更新平衡因子| b(0) b(-1) b先右单旋 c(1) a再左单旋 a(0) b(0) 更新平衡因子 // / \ / \ / \ \ / \ | / \ / \ / \ \ / // c(0) b(0) | c(0) b(0) // / \ | / c的右给b的左 / c的左给a的右 // 上叙情况节点c为新增节点 不管左右双旋 还是右左双旋 最后节点平衡因子都被更新为0 //②c不是新增节点(左右双旋的两种情况最后的平衡椅子更新都不一样) // a(-1) a(-2) // / \ / \ a(-2) e(0) // b(0) d(0) b(1) d(0) / \ / \ // / \ / \ / \ / \ e(-2) d(0) b(0) a(1) //g(0) e(0) h(0) j(0) g(-1)e(-1) h(0) j(0) b先左单旋 / \ / \ a再右单旋 / \ / \ 更新后a的平衡因子为1,b的平衡因子为0,e的平衡因子为0 // / / \ / / \ b(0) c(0) h(0) j(0) g(-1) f(-1) c(0) d(0) //i(0)f(0)c(0) i(0) f(-1) c(0) / \ / / / \ // / g(-1) f(-1) i(0) Q(0) h(0) j(0) // Q(0) / / // i(0) Q(0) // a(-1) a(-2) // / \ / \ a(-2) e(0) // b(0) d(0) b(1) d(0) / \ / \ // / \ / \ / \ / \ e(-1) d(0) b(-1) a(0) //g(0) e(0) h(0) j(0) g(-1)e(1) h(0) j(0) b先左单旋 / \ / \ a再右单旋 / \ / \ 更新后a的平衡因子为0,b的平衡因子为-1,e的平衡因子为0 // / / \ / / \ b(0) c(-1)h(0) j(0) g(-1) f(-1) c(-1) d(0) //i(0)f(0)c(0) i(0) f(0) c(-1) / \ / / / / \ // / g(-1) f(0)Q(0) i(0) Q(0) h(0) j(0) // Q(0) / // i(0) // 上叙情况节点c不是新增节点,Q是新增节点 左右双旋/右左双旋 最后节点平衡因子的更新:左右双旋/右左双旋是不同的。上方仅介绍了左右双旋,方法一样 //③c不是新增节点,q是新增节点(右左双旋的两种情况最后的平衡椅子更新都不一样,与左右双旋的方法一样这里不在介绍) // a(1) a(2) a(2) e(0) // / \ / \ / \ / \ // b(0) d(0) b(0) d(-1) b(0) e(1) a(0) d(1) // / \ / \ d先右旋 / \ a再左旋 / \ / \ 更新后a的平衡因子为0,d的平衡因子为1,e的平衡因子为0 // e(0) c(1) e(-1) c(0) q(0) d(1) b(0) q(0) c(0) // / / \ // q(0) c(0) // a(1) a(2) a(2) e(0) // / \ / \ / \ / \ // b(0) d(0) b(0) d(-1) b(0) e(2) a(-1) d(0) // / \ / \ d先右旋 / \ a再左旋 / \ / \ 更新后a的平衡因子为-1,d的平衡因子为0,e的平衡因子为0 // e(0) c(1) e(1) c(-1) d(0) b(0) q(0) c(0) // \ / \ // q(0) q(0 c(0) // // //折线插入:双旋 else if (parent->_bf == -2 && cur->_bf == 1) // 左右单旋 { RotateLR(parent);//从parent处左右双旋 } else if (parent->_bf == 2 && cur->_bf == -1) // 右左单旋 { RotateRL(parent);//从parent处右左双旋 } break; } else { assert(false);// 插入之前AVL树就存在不平衡的子树,|平衡因子| >= 2的节点 直接断言报错 } } return true; } void InOrder() { _InOrder(_root); cout << endl; } bool IsBalanceTree() { return _IsBalanceTree(_root); } int Height() { return _Height(_root); } private: Node* _root = nullptr; void RotateL(Node* parent)//左单旋 { Node* ppNode = parent->_parent; Node* subR = parent->_right; Node* subRL = subR->_left; parent->_right = subRL; if (subRL) { subRL->_parent = parent; } subR->_left = parent; parent->_parent = subR; if (parent == _root) { _root = subR; _root->_parent = nullptr;//subR->_parent = nullptr; //不可以只写这一句 如果parent是根 必须要更新_root; 加上 _root = subR; } else { if (parent == ppNode->_left) { ppNode->_left = subR; } else //parent == ppNode->_right { ppNode->_right = subR; } subR->_parent = ppNode; } //更新平衡因子 parent->_bf = 0; subR->_bf = 0; } void RotateR(Node* parent)//右单旋 { Node* ppNode = parent->_parent; Node* subL = parent->_left; Node* subLR = subL->_right; parent->_left = subLR; if (subLR) { subLR->_parent= parent; } subL->_right = parent; parent->_parent = subL; if (parent == _root) { _root = subL; _root->_parent = nullptr;//subR->_parent = nullptr; //不可以只写这一句 如果parent是根 必须要更新_root; 加上 _root = subR; } else { if (parent == ppNode->_left) { ppNode->_left = subL; } else //parent == ppNode->_right { ppNode->_right = subL; } subL->_parent = ppNode; } //更新平衡因子 parent->_bf = 0; subL->_bf = 0; } void RotateLR(Node* parent)//左右双旋 { Node* subL = parent->_left; Node* subLR = subL->_right; int bf = subLR->_bf; RotateL(subL);//RotateL(parent->_left); RotateR(parent); //更新平衡因子 if (bf == 0) { parent->_bf = 0; subL->_bf = 0; subLR->_bf = 0; } else if (bf == -1) { parent->_bf = 1; subL->_bf = 0; subLR->_bf = 0; } else if (bf == 1) { parent->_bf = 0; subL->_bf = -1; subLR->_bf = 0; } else { //subLR->_bf 旋转之前就有问题 assert(false); } } void RotateRL(Node* parent)//右左双旋 { Node* subR = parent->_right; Node* subRL = subR->_left; int bf = subRL->_bf; RotateR(subR);//RotateL(parent->_right); RotateL(parent); //更新平衡因子 if (bf == 0) { parent->_bf = 0; subR->_bf = 0; subRL->_bf = 0; } else if (bf == -1) { parent->_bf = 0; subR->_bf = 1; subRL->_bf = 0; } else if (bf == 1) { parent->_bf = -1; subR->_bf = 0; subRL->_bf = 0; } else { //subRL->_bf 旋转之前就有问题 assert(false); } } void _InOrder(Node* root) { if (root == nullptr) { return; } _InOrder(root->_left); cout << root->_kv.first << ":" << root->_kv.second << endl; _InOrder(root->_right); } int _Height(Node* root) { if (root == nullptr) return 0; int lh = _Height(root->_left);//lh leftheight 左树高度 int rh = _Height(root->_right);//rh rightheight 右树高度 return lh > rh ? lh + 1 : rh + 1; } bool _IsBalanceTree(Node* root) { // 空树也是AVL树 if (nullptr == root) return true; // 计算pRoot节点的平衡因子:即pRoot左右子树的高度差 int leftHeight = _Height(root->_left); int rightHeight = _Height(root->_right); int diff = rightHeight - leftHeight; // 如果计算出的平衡因子与pRoot的平衡因子不相等,或者 // pRoot平衡因子的绝对值超过1,则一定不是AVL树 if (abs(diff) >= 2) { cout << root->_kv.first << "节点平衡因子异常" << endl; return false; } if (diff != root->_bf) { cout << root->_kv.first << "节点平衡因子不符合实际" << endl; return false; } // pRoot的左和右如果都是AVL树,则该树一定是AVL树 return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right); } }; void TestAVLTree1() { //int a[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; int a[] = { 30,29,28,27,26,25,24,11,8,7,6,5,4,3,2,1 }; AVLTree<int, int> t; for (auto e : a) { t.insert(make_pair(e, e)); } t.InOrder(); cout<<t.IsBalanceTree()<<endl; } void TestAVLTree2() { int a[] = { 30,29,28,27,26,25,24,11,8,7,6,5,4,3,2,1 }; AVLTree<int, int> t; for (auto e : a) { bool res = t.insert(make_pair(e, e)); if (res) { cout << "Inserted: " << e << endl; } else { cout << "Failed to insert: " << e << endl; } } t.InOrder(); cout << t.IsBalanceTree() << endl; } int main() { //TestAVLTree1(); TestAVLTree2(); return 0; }
//插入节点后: // 若parent_bf: 由0 -->> -1 or 1高度改变,继续更新 // 若parent_bf:由-1 or 1 -->> -2 or 2 子树出现不平衡,需要旋转处理(左单旋/右单旋/左右双旋/右左双旋) else if (parent->_bf == 1 || parent->_bf == -1) //插入节点后导致一边变高了,parent_bf: 由0 -->> -1 or 1 高度改变,继续更新parent_bf (子树的高度变了,继续更新祖先) { // 0 0 | 0 0 // / \ / \ | / \ / \ // 0 0 插入右边 0 1 | 0 0 插入左边 -1 0 // \ \ | / / // 0 | 0 cur = cur->_parent; parent = parent->_parent; // 0 1 | 0 -1 // / \ / \ | / \ / \ // 0 1 更新parent_bf 0 1 | -1 0 更新parent_bf -1 0 //直到更新到根节点的祖先时才结束 (_root->_parent==nullptr 进不到while(cur){}循环里,所以结束) // \ \ | / / // 0 0 | 0 0 } else if (parent->_bf == 2 || parent->_bf == -2)//插入节点后导致本来高一边又变高了,出现了parent_bf=±2,parent_bf: 由-1 or 1 -->> -2 or 2 子树出现不平衡,需要旋转处理(左单旋/右单旋/左右双旋/右左双旋) { //直线插入:单旋 折线插入:双旋 //直线插入:单旋 // a(1) a(2) b(0) | a(-1) a(-2) b(0) // / \ / \ / \ | / \ / \ / \ // b(0) b(1) a左单旋,更新平衡因子 a(0) c(0) | b(0) b(-1) a右单旋,更新平衡因子 c(0) a(0) // \ \ \ | / / / // c(0) | c(0) if (parent->_bf == 2 && cur->_bf == 1) // 左单旋 { RotateL(parent);//parent左单旋,更新平衡因子 } else if (parent->_bf == -2 && cur->_bf == -1) // 右单旋 { RotateR(parent);//parent右单旋,更新平衡因子 } //折线插入:双旋 //①c为新增节点(左右双旋和右左双旋) // a(-1) a(-2) a(-2) c(0) | a(1) a(2) a(2) c(0) // / \ / \ / \ / \ | / \ / \ / \ / \ // b(0) b(1) b先左单旋 c(-1) a再右单旋 b(0) a(0) 更新平衡因子| b(0) b(-1) b先右单旋 c(1) a再左单旋 a(0) b(0) 更新平衡因子 // / \ / \ / \ \ / \ | / \ / \ / \ \ / // c(0) b(0) | c(0) b(0) // / \ | / c的右给b的左 / c的左给a的右 // 上叙情况节点c为新增节点 不管左右双旋 还是右左双旋 最后节点平衡因子都被更新为0 //②c不是新增节点(左右双旋的两种情况最后的平衡椅子更新都不一样) // a(-1) a(-2) // / \ / \ a(-2) e(0) // b(0) d(0) b(1) d(0) / \ / \ // / \ / \ / \ / \ e(-2) d(0) b(0) a(1) //g(0) e(0) h(0) j(0) g(-1)e(-1) h(0) j(0) b先左单旋 / \ / \ a再右单旋 / \ / \ 更新后a的平衡因子为1,b的平衡因子为0,e的平衡因子为0 // / / \ / / \ b(0) c(0) h(0) j(0) g(-1) f(-1) c(0) d(0) //i(0)f(0)c(0) i(0) f(-1) c(0) / \ / / / \ // / g(-1) f(-1) i(0) Q(0) h(0) j(0) // Q(0) / / // i(0) Q(0) // a(-1) a(-2) // / \ / \ a(-2) e(0) // b(0) d(0) b(1) d(0) / \ / \ // / \ / \ / \ / \ e(-1) d(0) b(-1) a(0) //g(0) e(0) h(0) j(0) g(-1)e(1) h(0) j(0) b先左单旋 / \ / \ a再右单旋 / \ / \ 更新后a的平衡因子为0,b的平衡因子为-1,e的平衡因子为0 // / / \ / / \ b(0) c(-1)h(0) j(0) g(-1) f(-1) c(-1) d(0) //i(0)f(0)c(0) i(0) f(0) c(-1) / \ / / / / \ // / g(-1) f(0)Q(0) i(0) Q(0) h(0) j(0) // Q(0) / // i(0) // 上叙情况节点c不是新增节点,Q是新增节点 左右双旋/右左双旋 最后节点平衡因子的更新:左右双旋/右左双旋是不同的。上方仅介绍了左右双旋,方法一样 //③c不是新增节点,q是新增节点(右左双旋的两种情况最后的平衡椅子更新都不一样,与左右双旋的方法一样这里不在介绍) // a(1) a(2) a(2) e(0) // / \ / \ / \ / \ // b(0) d(0) b(0) d(-1) b(0) e(1) a(0) d(1) // / \ / \ d先右旋 / \ a再左旋 / \ / \ 更新后a的平衡因子为0,d的平衡因子为1,e的平衡因子为0 // e(0) c(1) e(-1) c(0) q(0) d(1) b(0) q(0) c(0) // / / \ // q(0) c(0) // a(1) a(2) a(2) e(0) // / \ / \ / \ / \ // b(0) d(0) b(0) d(-1) b(0) e(2) a(-1) d(0) // / \ / \ d先右旋 / \ a再左旋 / \ / \ 更新后a的平衡因子为-1,d的平衡因子为0,e的平衡因子为0 // e(0) c(1) e(1) c(-1) d(0) b(0) q(0) c(0) // \ / \ // q(0) q(0 c(0) // // //折线插入:双旋