目录
两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年 发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(对树中的结点进行调整),即为AVl树以他们的名字缩写命名也可以叫高度二叉搜索树
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树,它就是AVL树。
如果AVl树有n个结点,其高度可保持在O(logN) ,搜索时间复杂度O(logN),为什么?
答:左右子树高度之差的绝对值不超过1,那么只有最后一层会差一部分的节点;
- template<class K, class V>
- struct AVLtreeNode
- {
- //节点构造函数
- AVLtreeNode(const pair
& kv) - :_left(nullptr)
- ,_right(nullptr)
- ,_parent(nullptr)
- ,_bf(0)
- ,_kv(kv)
- {}
- //节点的成员
- //三叉链
- AVLtreeNode
* _left; - AVLtreeNode
* _right; - AVLtreeNode
* _parent; - int _bf;//平衡因子
- //数据使用库里面的pair类存储的kv
- pair
_kv; - };
- template<class K,class V>
- class AVLtree
- {
- typedef AVLtreeNode
Node; - public:
- //构造函数
- AVLtree()
- :_root(nullptr)
- {}
- //四种旋转
- void RotateL(Node* parent)
- void RotateR(Node* parent)
- void RotateLR(Node* parent)
- void RotateRL(Node* parent)
- //插入
- bool Insert(const pair
& kv) - //寻找
- Node* Find(const K& kv)
- private:
- Node* _root;
- };
三叉链是什么?
- bool Insert(const pair
& kv) - {
- if (_root == nullptr)
- {
- _root = new Node(kv);
- return true;
- }
- Node* parent = _root, *cur = _root;
- while (cur)
- {
- //找nulptr,如果已经有这个key了,二叉搜索树的特性不支持冗余,所以返回失败
- if (cur->_kv.first > kv.first)
- {
- parent = cur;
- cur = cur->_left;
- }
- else if (cur->_kv.first
- {
- parent = cur;
- cur = cur->_right;
- }
- else
- {
- return false;
- }
- }
- //
- cur = new Node(kv);
- //判断孩子在父亲的左边还是右边
- if (cur->_kv.first > parent->_kv.first)
- {
- parent->_right = cur;
- cur->_parent = parent;
- }
- else
- {
- parent->_left = cur;
- cur->_parent = parent;
- }
- while (parent)
- {
- //影响一条路径所有的祖先
- if (parent->_right == cur)
- parent->_bf++;
- else
- parent->_bf--;
-
- if (parent->_bf == 0)
- {
- //左右平衡了不会再影响祖先了
- break;
- }
- if (parent->_bf == 1 || parent->_bf == -1)
- {
- //当前节点所在子树变了,会影响父亲
- // 继续往上更新
- 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)
- // 左单旋
- RotateL(parent);
- else // cur->_bf == -1
- RotateRL(parent);
- }
- break;
- }
- else
- {
- // 插入节点之前,树已经不平衡了,或者bf出错。需要检查其他逻辑
- assert(false);
- }
- }
- return true;
- }
插入整体逻辑:
- 如果还没有元素是一课空树,直接插入即可;如果有元素,按pair的first(key)和比较的节点比较结果为大说明为空的哪个位置在右边,和比较的节点比较的结果小说明为空的哪个位置在左边,如果相等说明已经有这个元素了,二叉搜索树不支持冗余返回一个pair类第一个成员为那个相同元素的map的迭代器和第二个成员为false的pair类迭代器;
- 不知道这个已经找到的位置在父节点的左边还是右边,需要判断一下,然后插入元素;
- 插入元素的后那么平衡因子将发生变化,为0说明这个父亲节点左右平衡不会影响其他节点,为1或者-1需要向上调整,为2或者-2说明已经不平衡需要旋转;
- 节点右子树最长路径-左子树最长路径,右边插入节点就+,左边插入节点就-;
3.1四种旋转(左单旋、右单旋、左右双旋、右左双旋)
3.1.1左单旋
- 调用函数是传的参数是轴点
- 要保留轴点的父亲,以及调整三叉链
- 调整后原来的孩子和父亲(轴点)的平衡因子都置为0;
- void RotateR(Node* parent)
- {
- //轴点的左,孩子节点
- Node* subL = parent->_left;
- //孩子节点的右
- Node* subLR = subL->_right;
- //我的右当你(轴点)的左
- parent->_left = subLR;
- //调整三叉链
- if (subLR)
- subLR->_parent = parent;
- //你(轴点)做我的右
- subL->_right = parent;
- //调整三叉链
- Node* parentParent = parent->_parent;
- parent->_parent = subL;
-
- if (parent == _root)
- {
- _root = subL;
- _root->_parent = nullptr;
- }
- else
- {
- //轴点的父亲新的孩子节点
- if (parentParent->_left == parent)
- parentParent->_left = subL;
- else
- parentParent->_right = subL;
-
- subL->_parent = parentParent;
- }
-
- subL->_bf = parent->_bf = 0;
- }
3.1.2右单旋
- 调用函数是传的参数是轴点
- 要保留轴点的父亲,以及调整三叉链
- 调整后原来的孩子和父亲(轴点)的平衡因子都置为0;
- void RotateL(Node* parent)
- {
- //轴点的右,孩子节点
- Node* subR = parent->_right;
- //孩子节点的左
- Node* subRL = subR->_left;
- //我的左当你(轴点)的右
- parent->_right = subRL;
- //调整三叉链
- if (subRL)
- {
- subRL->_parent = parent;
- }
- //你(轴点)做我的左
- subR->_left = parent;
- Node* parentparent = parent->_parent;
-
- parent->_parent = subR;
- if (parent == _root)
- {
- if (parentparent->_left == parent)
- parentparent->_left = subR;
- else
- parentparent->_right = subR;
-
- subR->_parent = parentparent;
- }
- else
- {
- subR->_parent = nullptr;
- _root = subR;
- }
-
- subR->_bf = parent->_bf = 0;
-
- }
3.1.3左右双旋
- 调用左单旋是传的参数是轴点1,右单旋传的轴点2
- 平衡因子分3种情况,依靠3个被改变节点中最后一个来判断
- void RotateLR(Node* parent)
- {
- Node* subL = parent->_left;
- Node* subLR = subL->_right;
- int bf = subLR->_bf;
-
- RotateL(parent->_left);
- RotateR(parent);
-
- // ...平衡因子调节还需要具体分析
- if (bf == -1)
- {
- subL->_bf = 0;
- parent->_bf = 1;
- subLR->_bf = 0;
- }
- else if (bf == 1)
- {
- parent->_bf = 0;
- subL->_bf = -1;
- subLR->_bf = 0;
- }
- else if (bf == 0)
- {
- parent->_bf = 0;
- subL->_bf = 0;
- subLR->_bf = 0;
- }
- else
- {
- assert(false);
- }
- }
依靠3个被改变节点中最后一个来判断
3.1.4右左双旋
- 调用右单旋是传的参数是轴点1,左单旋传的轴点2
- 平衡因子分3种情况,依靠3个被改变节点中最后一个来判断
- void RotateRL(Node* parent)
- {
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
- int bf = subRL->_bf;
-
- RotateR(parent->_right);
- RotateL(parent);
-
- // 平衡因子更新
- 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 = 0;
- subR->_bf = 0;
- subRL->_bf = 0;
- }
- else
- {
- assert(false);
- }
- }
总结
- 调用旋转的实参是轴点
- 左单旋:我的左当你的右,你(轴点)当我的左
- 右单旋:我的右当你的左,你(轴点)当我的右