目录
1. 左单旋转 --- RL(RotateLeft) 型调整操作
2. 右单旋转 --- RR(RotateRight) 型调整操作
3. 先左后右双旋转 --- RLR (RotateLeftRight) 型调整操作
4. 先右后左双旋转 --- RRL (RotateRightLeft) 型调整操作
AVL树,又称平衡二叉树。
它可以是一颗空树,或者是具有以下性质的二叉树:即它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度只差的绝对值不超过 1。 把二叉树上结点的平衡因子BF定义为该结点的左子树的高度和右子树的高度之差(即平衡二叉树上结点的平衡因子只可能是 -1、0 和 1)
只要二叉树上有一个结点的平衡因子的绝对值大于1,则该二叉树就是不平衡的。
说明:后面所用到的平衡因子的都是右子树的高的 - 左子树的高度
影响二叉搜索树平衡的操作只能是插入和删除,这里已插入为例,同样一组数据元素插 入的顺序不同,二叉搜索树的形状就不同。也就需要一种动态平衡方法,当插入操作破坏了平衡,便可以用来调整。这种方式需要在原来二叉搜索树结点中增加一个量为平衡因子(BF)
结点结构图:
在这里为了方便进行旋转操作对于AVL树的结点定义采用三叉链的结构:
- //类模板结点的定义
- template <class T>
- struct AVLTreeNode
- {
- AVLTreeNode
* _left; - AVLTreeNode
* _right; - AVLTreeNode
* _parent; //指向当前结点的父节点的指针 - AVLTreeNode
_data; - int _bf; //平衡因子
-
- //结点的构造函数
- AVLTreeNode(const T& data)
- :_left(nullptr)
- ,_right(nullptr)
- ,_parent(nullptr)
- ,_bf(0)
- ,_data(data)
- {}
-
- };
如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构, 使之平衡化。根据节点插入位置的不同,AVL树的旋转分为以下四种:
单向左旋平衡处理: 由于在subR这个结点的右子树上插入结点 ,subR的平衡因子由0变为1,p的平衡因子由1变为2,导致以p为根的子树失去平衡,则需进行一次向左的逆时针旋转操作
链接操作:b链接到p的右;
p链接到subR的左;
subR成为当前树的根
注意:1. 链接时subRL为空的情况
2. p可能是整棵树的子树 (p的上面可能还有结点) 或 整棵树的根 (p的上面无结点)
图示:

- //左单旋转
- void RotateL(Node* parent)
- {
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
-
- //做左旋转(修改结点的指向)
- parent->_right = subRL;
- if (subRL) //若subRL不为空,则修改subRL中指向父节点的指针(_parent)
- subRL->_parent = parent;
- subR->_left = parent;
-
- //修改各结点中父指针(_parent)的指向
- Node* ppnode = parent->_parent; //保存parent中父指针(_parent)的指向
- parent->_parent = subR; //修改parent中指向父节点的指针(_parent)
-
- if (parent == _root) //判断当前结点是否为根节点
- {
- _root = subR;
- subR->_parent = nullptr;
- }
- else
- {
- if (ppnode->_right == parent)
- {
- ppnode->_right = subR;
- }
- else
- {
- ppnode->_left = subR;
- }
- subR->_parent = ppnode;
- }
- //更新平衡因子
- parent->_bf = 0;
- subR->_bf = 0;
- }
单向右旋平衡处理: 由于在subL的左子树上插入结点,subL的平衡因子由 0变成 -1 ,p的平衡因子由 1 变为 -2,导致以p为根的子树失去平衡,则需进行一次向右的顺时针旋转操作。
链接操作:b链接到p的左;
p链接到subL的右;
subL成为当前树的根
注意:1. 链接时subLR为空的情况
2. p可能是整棵树的子树 (p的上面可能还有结点) 或 整棵树的根 (p的上面无结点)
图示:

- //右单旋转
- void RotateR(Node* parent)
- {
- Node* subL = parent->_left;
- Node* subLR = subL->_right;
-
- //做右旋转(修改结点的指向)
- parent->_left = subLR;
- if (subLR) //若subLR不为空,则修改subLR中指向父节点的指针(_parent)
- subLR->_parent = parent;
- subL->_right = parent;
-
- //修改各结点中父指针(_parent)的指向
- Node* ppnode = parent->_parent; //保存parent中父指针(_parent)的指向
- parent->_parent = subL; //修改parent中指向父节点的指针(_parent)
-
- if (parent == _root) //判断当前结点是否为根节点
- {
- _root = subL;
- subL->_parent = nullptr;
- }
- else
- {
- if (ppnode->_left == parent)
- {
- ppnode->_left = subL;
- }
- else
- {
- ppnode->_right = subL;
- }
- subL->_parent = ppnode;
- }
-
- //更改平衡因子
- parent->_bf = 0;
- subL->_bf = 0;
- }
双向旋转(先左后右)平衡处理:由于在subL的右子树上插入结点,subL的平衡因子由 0 变为 1,p的平衡因子由 -1 变为 -2,导致以p为根的子树失去平衡,则需进行两次旋转(先左旋后右旋)操作
链接操作:左单旋:b链接到subL的右;
subL链接到subLR的左;
subLR链接到p的左
右单旋:c链接到p的左;
p链接到subLR的右;
subLR成为当前子树的根
图示:

- //先左后右双旋转
- void RotateLR(Node* parent)
- {
- Node* subL = parent->_left;
- Node* subLR = subL->_right;
-
- int bf = subLR->_bf; //先保存旋转前subLR结点的平衡因子
- RotateL(parent->_left); //左单旋转
- RotateR(parent); //右单旋转
-
- //更新旋转后的平衡因子
- if (bf == -1)
- {
- subLR->_bf = 0;
- subL->_bf = 0;
- parent->_bf = 1;
- }
- else if (bf == 1)
- {
- subLR->_bf = 0;
- subL->_bf = -1;
- parent->_bf = 0;
- }
- else if (bf == 0)
- {
- subLR->_bf = 0;
- subL->_bf = 0;
- parent->_bf = 0;
- }
- else
- {
- assert(false);
- }
- }
双向旋转(先右后左)平衡处理:由于在subR的左子树上插入结点,subR的平衡因子由 0 变为 -1,p的平衡因子由 1 变为 2,导致以p为根的子树失去平衡,则需进行两次旋转(先右旋后左旋)操作
链接操作:右单旋:c链接到subR的左;
subR链接到subRL的右;
subRL链接到p的右
左单旋:b链接到p的右;
p链接到subRL的左;
subRL成为当前子树的根
图示:

- //先右后左双旋转
- void RotateRL(Node* parent)
- {
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
-
- int bf = subRL->_bf; //先保存旋转前subRL结点的平衡因子
- RotateR(subR); //右单旋转
- RotateL(parent); //左单旋转
-
- //更新旋转后的平衡因子
- if (bf == 1)
- {
- subRL->_bf = 0;
- subR->_bf = 0;
- parent->_bf = -1;
- }
- else if (bf == -1)
- {
- subRL->_bf = 0;
- subR->_bf = 1;
- parent->_bf = 0;
- }
- else if(bf==0)
- {
- subRL->_bf = 0;
- subR->_bf = 0;
- parent->_bf = 0;
- }
- else
- {
- assert(false);
- }
- }
AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么 AVL树的插入过程可以分为两步:
1. 按照二叉搜索树的方式插入新节点
2. 调整节点的平衡因子
插入一个结点cur(当前要插入的结点)后,Parent的平衡因子一定需要调整,在插入之前,Parent 的平衡因子分为三种情况:-1,0, 1;
分以下两种情况:
1. 如果cur插入到Parent的左侧,只需给 Parent 的平衡因子 减1 即可
2. 如果cur插入到Parent的右侧,只需给 Parent 的平衡因子 加1 即可
此时:Parent的平衡因子可能有三种情况:0,1或-1, 2或-2
1. 如果Parent的平衡因子为 0,说明插入之前Parent的平衡因子为 1或-1,插入后被调整成 0,此时满足 AVL树的性质,插入成功
2. 如果Parent的平衡因子为 1或-1,说明插入前Parent的平衡因子一定为 0,插入后被更新成 1或-1,此时,以Parent为根的树的高度增加,需要继续向上更新
3. 如果Parent的平衡因子为 2或-2,则Parent的平衡因子违反平衡树的性质,需要对其进行旋转处理
- bool Insert(const T& data)
- {
- //为空树
- if (_root == nullptr)
- {
- _root = new Node(data);
- return true;
- }
-
- //按照二叉搜索树的规则插入结点
- Node* parent = nullptr; //记录插入结点的父节点
- Node* cur = _root;
- while (cur)
- {
- if (cur->_data < data)
- {
- parent = cur;
- cur = cur->_right;
- }
- else if (cur->_data > data)
- {
- parent = cur;
- cur = cur->_left;
- }
- else
- {
- return false;
- }
- }
-
- //判断链接到父节点的那边
- cur = new Node(data);
- if (parent->_data > data)
- {
- parent->_left = cur;
- }
- else
- {
- parent->_right = cur;
- }
-
- //调整平衡因子 及 旋转调整
- cur->_parent = parent; //修改当前结点(cur)的父指针(_parent)的指向
- while (parent)
- {
- if (cur == parent->_left) //插入到父结点的左边,父节点的平衡因子--
- {
- parent->_bf--;
- }
- else //插入到父结点的左边,父节点的平衡因子--
- {
- parent->_bf++;
- }
- //
- if (parent->_bf == 0) //若(插入节点后)当前结点的平衡因子为零,则不会影响此节点的父及祖先结点;
- { //说明当前这颗子树插入节点后,其高度没有发生变化,也就不需要向上更新平衡因子
- break;
- }
- else if (parent->_bf == 1 || parent->_bf == -1) //若当前结点的平衡因子为1或-1,则会影响上面的祖先结点的平衡因子
- { //需要更新上面祖先的平衡因子
- cur = cur->_parent;
- parent = parent->_parent;
- }
- else if (parent->_bf == 2 || parent->_bf == -2)//若当前结点的平衡因子为2或-2,则需做旋转调整
- {
- //旋转
- if (parent->_bf == 2 && cur->_bf == 1)
- {
- RotateL(parent); //左单旋转
- }
- else if (parent->_bf == -2 && cur->_bf == -1)
- {
- RotateR(parent); //右单旋转
- }
- else if (parent->_bf == -2 && cur->_bf == 1)
- {
- RotateLR(parent); //先左单旋转,在右单旋转
- }
- else
- {
- RotateRL(parent); //先右单旋转,在左单旋转
- }
-
- break;
- }
- else //说明插入之前AVL数就有问题
- {
- assert(false);
- }
- }
- return true;
- }
AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:
1. 验证其为二叉搜索树 如果中序遍历可得到一个有序的序列,就说明为二叉搜索树
验证方法:采用中序遍历即可
2. 验证其为平衡树 每个节点子树高度差的绝对值不超过1
(注意节点中如果没有平衡因子) 节点的平衡因子是否计算正确
验证方法:1.验证每颗子树的左右高度差的绝对值是否超过 1(采用递归思想);
2.验证结点的平衡因子是否正确
- //中序遍历
- void InOrder(Node* root)
- {
- if (root == nullptr)
- return;
- InOrder(root->_left);
- cout << root->_data << endl;
- InOrder(root->_right);
- }
-
-
- //求树的高度
- int _Height(Node* root)
- {
- if (root == nullptr)
- return 0;
- int leftheight = _Height(root->_left);
- int rightheight = _Height(root->_right);
- return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
- }
-
-
- // 验证AVL树的平衡
- bool _IsAVLTree1(Node* root) //前序遍历
- {
- if (root == nullptr)
- return true;
-
- int leftheight = _Height(root->_left); //左子树的高度
- int rightheight = _Height(root->_right); //右子树的高度
-
- //判断左右高度差是否超过1
- if (abs(rightheight - leftheight) >= 2)
- {
- cout << root->_data << "不平衡" << endl;
- return false;
- }
-
- //判断结点的平衡因子是否有异常
- if (rightheight - leftheight != root->_bf)
- {
- cout << root->_data << "平衡因子异常" << endl;
- return false;
- }
-
- return _IsAVLTree1(root->_left) && _IsAVLTree1(root->_right);
- }
- template <class T>
- struct AVLTreeNode
- {
- AVLTreeNode
* _left; - AVLTreeNode
* _right; - AVLTreeNode
* _parent; //指向当前结点的父节点的 - int _bf; //平衡因子
- T _data;
-
- AVLTreeNode(const T& data)
- :_left(nullptr)
- ,_right(nullptr)
- ,_parent(nullptr)
- ,_bf(0)
- ,_data(data)
- {}
-
- };
-
- template<class T>
- class AVLTree
- {
- typedef AVLTreeNode
Node; - public:
-
- AVLTree()
- : _root(nullptr)
- {}
-
- bool Insert(const T& data)
- {
- if (_root == nullptr)
- {
- _root = new Node(data);
- return true;
- }
-
- Node* parent = nullptr;
- Node* cur = _root;
- while (cur)
- {
- if (cur->_data < data)
- {
- parent = cur;
- cur = cur->_right;
- }
- else if (cur->_data > data)
- {
- parent = cur;
- cur = cur->_left;
- }
- else
- {
- return false;
- }
- }
-
- cur = new Node(data);
- if (parent->_data > data)
- {
- parent->_left = cur;
- }
- else
- {
- parent->_right = cur;
- }
-
- //调整平衡因子 及 旋转调整
- cur->_parent = parent;
- while (parent)
- {
- if (cur == parent->_left) //插入到父结点的左边,父节点的平衡因子--
- {
- parent->_bf--;
- }
- else //插入到父结点的左边,父节点的平衡因子--
- {
- parent->_bf++;
- }
-
- if (parent->_bf == 0)
- {
- break;
- }
- else if (parent->_bf == 1 || parent->_bf == -1)
- { //需要更新上面祖先的平衡因子
- cur = cur->_parent;
- parent = parent->_parent;
- }
- else if (parent->_bf == 2 || parent->_bf == -2)
- {
- //旋转
- if (parent->_bf == 2 && cur->_bf == 1)
- {
- RotateL(parent); //左单旋转
- }
- else if (parent->_bf == -2 && cur->_bf == -1)
- {
- RotateR(parent); //右单旋转
- }
- else if (parent->_bf == -2 && cur->_bf == 1)
- {
- RotateLR(parent); //先左单旋转,在右单旋转
- }
- else
- {
- RotateRL(parent); //先右单旋转,在左单旋转
- }
-
- break;
- }
- else //说明插入之前AVL数就有问题
- {
- assert(false);
- }
- }
- return true;
- }
-
- //左单旋转
- void RotateL(Node* parent)
- {
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
-
- //做左旋转(修改结点的指向)
- parent->_right = subRL;
- if (subRL) //若subRL不为空,则修改subRL中指向父节点的指针(_parent)
- subRL->_parent = parent;
- subR->_left = parent;
-
- //修改各结点中父指针(_parent)的指向
- Node* ppnode = parent->_parent; //保存parent中父指针(_parent)的指向
- parent->_parent = subR; //修改parent中指向父节点的指针(_parent)
-
- if (parent == _root) //判断当前结点是否为根节点
- {
- _root = subR;
- subR->_parent = nullptr;
- }
- else
- {
- if (ppnode->_right == parent)
- {
- ppnode->_right = subR;
- }
- else
- {
- ppnode->_left = subR;
- }
- subR->_parent = ppnode;
- }
- //更新平衡因子
- parent->_bf = 0;
- subR->_bf = 0;
- }
-
- //右单旋转
- void RotateR(Node* parent)
- {
- Node* subL = parent->_left;
- Node* subLR = subL->_right;
-
- //做右旋转(修改结点的指向)
- parent->_left = subLR;
- if (subLR) //若subLR不为空,则修改subLR中指向父节点的指针(_parent)
- 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;
- }
- else
- {
- 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(parent->_left);
- RotateR(parent);
-
- //更新旋转后的平衡因子
- if (bf == -1)
- {
- subLR->_bf = 0;
- subL->_bf = 0;
- parent->_bf = 1;
- }
- else if (bf == 1)
- {
- subLR->_bf = 0;
- subL->_bf = -1;
- parent->_bf = 0;
- }
- else if (bf == 0)
- {
- subLR->_bf = 0;
- subL->_bf = 0;
- parent->_bf = 0;
- }
- else
- {
- assert(false);
- }
- }
-
- //先右后左双旋转
- void RotateRL(Node* parent)
- {
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
-
- int bf = subRL->_bf;
- RotateR(subR);
- RotateL(parent);
-
- //更新旋转后的平衡因子
- if (bf == 1)
- {
- subRL->_bf = 0;
- subR->_bf = 0;
- parent->_bf = -1;
- }
- else if (bf == -1)
- {
- subRL->_bf = 0;
- subR->_bf = 1;
- parent->_bf = 0;
- }
- else if(bf==0)
- {
- subRL->_bf = 0;
- subR->_bf = 0;
- parent->_bf = 0;
- }
- else
- {
- assert(false);
- }
- }
-
- //高度
- int Height()
- {
- return _Height(_root);
- }
-
- // AVL树的验证
- bool IsAVLTree()
- {
- return _IsAVLTree1(_root);
- }
-
- //中序遍历
- void InOrder()
- {
- _InOrder(_root);
- }
-
- private:
- int _Height(Node* root)
- {
- if (root == nullptr)
- return 0;
- int leftheight = _Height(root->_left);
- int rightheight = _Height(root->_right);
- return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
- }
-
- // AVL树的验证
- bool _IsAVLTree1(Node* root) //前序遍历
- {
- if (root == nullptr)
- return true;
-
- int leftheight = _Height(root->_left);
- int rightheight = _Height(root->_right);
-
- if (abs(rightheight - leftheight) >= 2)
- {
- cout << root->_data << "不平衡" << endl;
- return false;
- }
-
- if (rightheight - leftheight != root->_bf)
- {
- cout << root->_data << "平衡因子异常" << endl;
- return false;
- }
-
- return _IsAVLTree1(root->_left) && _IsAVLTree1(root->_right);
- }
-
- //中序遍历
- void _InOrder(Node* root)
- {
- if (root == nullptr)
- return;
- _InOrder(root->_left);
- cout << root->_data << endl;
- _InOrder(root->_right);
- }
-
- private:
- Node* _root;
- };