目录
①二叉搜索树虽然可以提高我们查找数据的效率,但如果插入二叉搜索树的数据是有序或接近有序的,此时二叉搜索树会退化为单支树,在单支树当中查找数据相当于在单链表当中查找数据,效率是很低下的。
②因此,两位俄罗斯的数学家G.M.A delson-Velskii和E.M.Landis在1962年发明了解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
③AVL树可以是一棵空树,也可以是具有以下性质的一棵二叉搜索树:

④如果一棵二叉搜索树的高度是平衡的,它就是AVL树。如果它有n个结点,其高度可保持在)O(logN),搜索时间复杂度也是O(logN)。
⑤注意: 这里所说的二叉搜索树的高度是平衡的是指,树中每个结点左右子树高度之差的绝对值不超过1,因为只有满二叉树才能做到每个结点左右子树高度之差均为0。
给每个结点增加平衡因子并不是必须的,只是实现AVL树的一种方式,不引入平衡因子也可以实现AVL树,只不过会麻烦一点。
- template<class K, class V>
- struct AVLTreeNode
- {
- //三叉链
- AVLTreeNode
* _left; - AVLTreeNode
* _right; - AVLTreeNode
* _parent; -
- //存储的键值对
- pair
_kv; -
- //平衡因子(balance factor)
- int _bf; //右子树高度-左子树高度,我们只是这样规定的
-
- //构造函数
- AVLTreeNode(const pair
& kv) - :_left(nullptr)
- , _right(nullptr)
- , _parent(nullptr)
- , _kv(kv)
- , _bf(0)
- {}
- };
(1)AVL树插入结点时有以下三个步骤:
(2)平衡因子

①所以我们插入结点后需要倒着往上更新平衡因子,更新规则如下:
②每更新完一个结点的平衡因子后,都需要进行以下判断:
③判断理由
④注意

⑤说明
⑥我们将插入结点称为cur,将其父结点称为parent,那么我们更新平衡因子时第一个更新的就是parent结点的平衡因子,更新完parent结点的平衡因子后,若是需要继续往上进行平衡因子的更新,那么我们必定要执行以下逻辑:
- cur = parent;
- parent = parent->_parent;
⑦代码
- //插入函数
- bool Insert(const pair
& kv) - {
- if (_root == nullptr) //若AVL树为空树,则插入结点直接作为根结点
- {
- _root = new Node(kv);
- return true;
- }
-
- //1、按照二叉搜索树的插入方法,找到待插入位置
- Node* cur = _root;
- Node* parent = nullptr;
- while (cur)
- {
- if (kv.first < cur->_kv.first) //待插入结点的key值小于当前结点的key值
- {
- //往该结点的左子树走
- parent = cur;
- cur = cur->_left;
- }
- else if (kv.first > cur->_kv.first) //待插入结点的key值大于当前结点的key值
- {
- //往该结点的右子树走
- parent = cur;
- cur = cur->_right;
- }
- else //待插入结点的key值等于当前结点的key值
- {
- //插入失败(不允许key值冗余)
- return false;
- }
- }
-
- //2、将待插入结点插入到树中
- cur = new Node(kv); //根据所给值构造一个新结点
- if (kv.first < parent->_kv.first) //新结点的key值小于parent的key值
- {
- //插入到parent的左边
- parent->_left = cur;
- cur->_parent = parent;
- }
- else //新结点的key值大于parent的key值
- {
- //插入到parent的右边
- parent->_right = cur;
- cur->_parent = parent;
- }
-
- //3、更新平衡因子,如果出现不平衡,则需要进行旋转
- while (cur != _root) //最坏一路更新到根结点
- {
- if (cur == parent->_left) //parent的左子树增高
- {
- parent->_bf--; //parent的平衡因子--
- }
- else if (cur == parent->_right) //parent的右子树增高
- {
- parent->_bf++; //parent的平衡因子++
- }
-
- //判断是否更新结束或需要进行旋转
- if (parent->_bf == 0) //更新结束(新增结点把parent左右子树矮的那一边增高了,此时左右高度一致)
- {
- break; //parent树的高度没有发生变化,不会影响其父结点及以上结点的平衡因子
- }
- else if (parent->_bf == -1 || parent->_bf == 1) //需要继续往上更新平衡因子
- {
- //parent树的高度变化,会影响其父结点的平衡因子,需要继续往上更新平衡因子
- cur = parent;
- parent = parent->_parent;
- }
- else if (parent->_bf == -2 || parent->_bf == 2) //需要进行旋转(此时parent树已经不平衡了)
- {
- if (parent->_bf == -2)
- {
- if (cur->_bf == -1)
- {
- RotateR(parent); //右单旋
- }
- else //cur->_bf == 1
- {
- RotateLR(parent); //左右双旋
- }
- }
- else //parent->_bf == 2
- {
- if (cur->_bf == -1)
- {
- RotateRL(parent); //右左双旋
- }
- else //cur->_bf == 1
- {
- RotateL(parent); //左单旋
- }
- }
-
- break; //旋转后就一定平衡了,无需继续往上更新平衡因子(旋转后树高度变为插入之前了)
- }
- else
- {
- assert(false); //在插入前树的平衡因子就有问题
- }
- }
-
- return true; //插入成功
- }
(1)左单旋

①左单旋步骤
②左单旋后满足二叉搜索树的性质:
③代码 :结点是三叉链结构,改变结点关系时需要跟着改变父指针的指向。
- //左单旋
- void RotateL(Node* parent)
- {
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
-
-
- //1、建立parent和subRL之间的关系
- parent->_right = subRL;
- if (subRL) //subRL可能为空
- subRL->_parent = parent;
-
- //2.记录pparent
- Node* pparent = parent->_parent;
-
- //3.建立parent和subR的关系
- subR->_left = parent;
- parent->_parent = subR;
-
- //4.建立pparent和subR的关系
- if (pparent == nullptr) //parent为根,是一颗单独的树
- {
- _root = subR;
- subR->_parent = nullptr; //subR的_parent指向需改变
- }
- else
- {
- if (parent == pparent->_left)
- {
- pparent->_left = subR;
- }
- else //parent == pparent->_right
- {
- pparent->_right = subR;
- }
-
- subR->_parent = pparent;
- }
-
- //5、更新平衡因子
- subR->_bf = parent->_bf = 0;
- }
(2)右单旋

①右单旋的步骤如下:
②右单旋后满足二叉搜索树的性质:
③代码 : 结点是三叉链结构,改变结点关系时需要跟着改变父指针的指向。
- void RotateR(Node* parent)
- {
-
-
- Node* subL = parent->_left;
- Node* subLR = subL->_right;
-
- //1.建立parent和subLR的关系
- parent->_left = subLR;
- if (subLR) //subLR可能为空
- {
- subLR->_parent = parent;
- }
-
- //2.记录pparent
- Node* pparent = parent->_parent;
-
- //3.建立subL和parent的关系
- subL->_right = parent;
- parent->_parent = subL;
-
-
- //4.建立pparen和subL的关系
- if (parent == _root) //parent是一颗独立的树
- {
- _root = subL;
- _root->_parent = nullptr;
- }
- else //parent是一颗子树,
- {
- if (pparent->_left == parent)
- {
- pparent->_left = subL;
- }
- else
- {
- pparent->_right = subL;
- }
- subL->_parent = pparent; //改subL的parent
- }
-
- //5.更新平衡因子
- subL->_bf = parent->_bf = 0; //更新平衡因子
-
- }
(3)左右双旋

①左右双旋的步骤如下:
②左右双旋后满足二叉搜索树的性质:
左右双旋后,实际上就是让subLR的左子树和右子树,分别作为subL和parent的右子树和左子树,再让subL和parent分别作为subLR的左右子树,最后让subLR作为整个子树的根(结合图理解)。
③左右双旋后,平衡因子的更新随着subLR原始平衡因子的不同分为以下三种情况:



④代码
- void RotateLR(Node* parent)
- {
- Node* subL = parent->_left;
- Node* subLR = subL->_right;
-
- int bf = subLR->_bf; //subLR不可能为nullptr,因为subL的平衡因子是1
-
- //1.以subL为旋转点进行左单旋
- RotateL(subL);
-
- //2.以parent为旋转点进行右单旋
- RotateR(parent);
-
- //3.平衡因子的调节
- if (bf == -1)
- {
- subL->_bf = subLR->_bf = 0;
- parent->_bf = 1;
- }
- else if (bf == 1)
- {
- subLR->_bf = parent->_bf = 0;
- subL->_bf = -1;
- }
- else if (bf == 0)
- {
- subL->_bf = subLR->_bf = parent->_bf = 0;
- }
- else
- {
- assert(false);
- }
- }
(4)右左双旋

①右左双旋的步骤如下:
②右左双旋后满足二叉搜索树的性质:
右左双旋后,实际上就是让subRL的左子树和右子树,分别作为parent和subR的右子树和左子树,再让parent和subR分别作为subRL的左右子树,最后让subRL作为整个子树的根(结合图理解)。
③右左双旋后,平衡因子的更新随着subLR原始平衡因子的不同分为以下三种情况:



④代码
- void RotateRL(Node* parent)
- {
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
-
- int bf = subRL->_bf;
-
- //1.以subR为轴进行右旋
- RotateR(subR);
-
- //2.以parent为轴进行左旋
- RotateL(parent);
-
- //3.更新平衡因子
- if (bf == 1)
- {
- subR->_bf = 0;
- parent->_bf = -1;
- subRL->_bf = 0;
- }
- else if (bf == -1)
- {
- parent->_bf = 0;
- subR->_bf = 1;
- subRL->_bf = 0;
- }
- else if (bf == 0)
- {
- parent->_bf = subR->_bf = subRL->_bf = 0;
- }
- else
- {
- assert(false);
- }
- }
(1)验证是否是二叉搜索树
- //中序遍历
- void Inorder()
- {
- _Inorder(_root);
- }
-
- //中序遍历子函数
- void _Inorder(Node* root)
- {
- if (root == nullptr)
- return;
- _Inorder(root->_left);
- cout << root->_kv.first << " ";
- _Inorder(root->_right);
- }
(2)验证平衡因子

- //判断是否为AVL树
- bool IsAVLTree()
- {
- int hight = 0; //输出型参数
- return _IsBalanced(_root, hight);
- }
-
- //检测二叉树是否平衡
- bool _IsBalanced(Node* root, int& hight)
- {
- if (root == nullptr) //空树是平衡二叉树
- {
- hight = 0; //空树的高度为0
- return true;
- }
-
- //先判断左子树
- int leftHight = 0;
- if (_IsBalanced(root->_left, leftHight) == false)
- return false;
-
- //再判断右子树
- int rightHight = 0;
- if (_IsBalanced(root->_right, rightHight) == false)
- return false;
-
- //检查该结点的平衡因子
- if (rightHight - leftHight != root->_bf)
- {
- cout << "平衡因子设置异常:" << root->_kv.first << endl;
- }
-
- //把左右子树的高度中的较大值+1作为当前树的高度返回给上一层
- hight = max(leftHight, rightHight) + 1;
- return abs(rightHight - leftHight) < 2; //平衡二叉树的条件
- }
- //查找函数
- Node* Find(const K& key)
- {
- Node* cur = _root;
- while (cur)
- {
- if (key < cur->_kv.first) //key值小于该结点的值
- {
- cur = cur->_left; //在该结点的左子树当中查找
- }
- else if (key > cur->_kv.first) //key值大于该结点的值
- {
- cur = cur->_right; //在该结点的右子树当中查找
- }
- else //找到了目标结点
- {
- return cur; //返回该结点
- }
- }
- return nullptr; //查找失败
- }