
"我们无法一直活在过去,亦如我们从未能挽救死亡"
①根节点的左右子树 是AVL树
②左右子树的高度的差 不超过1
③比根节点大的 插在根节点的右边。 小的 在左边。

关于AVL树概念上的东西,多讲无用。我们现在直接来实现。
注:这里实现的是key值版。
- //AVL树的 节点
- template<class T>
- struct AVLTreeNode
- {
- //节点指针
- AVLTreeNode
* _left; - AVLTreeNode
* _right; - AVLTreeNode
* _parent; - //节点数值
- T _key;
-
- //为了调节平衡 这里引入平衡因子;
- int _bf;
-
- //构造
- AVLTreeNode(const T& key)
- :_left(nullptr),
- : _right(nullptr),
- _parent(nullptr),
- _key(key),
- _bf(0) //任何新插入的 节点 平衡因子都默认设置为0
- {}
-
- };
-
- template<class K>
- class AVLTree
- {
- typedef AVLTreeNode
Node; -
- AVLTree()
- :_root(nullptr)
- {}
-
- private:
- Node* _root;
- };
对于AVL树而言,显然难点在插入的部分。所以这留到最后讲。那么这部分进行相关功能的实现。

- Node* find(const K& key)
- {
- Node* cur = _root;
- while (cur)
- {
- if (key > cur->_key)
- {
- cur = cur->_right;
- }
- else if(key < cur->_key)
- {
- cur = cur->_left;
- }
- else
- {
- //找到了
- return cur;
- }
- }
- return nullptr;
- }
-
-
- void _InOrder(Node* root)
- {
- if (root == nullptr)
- return;
-
- _InOrder(root->_left);
- cout << root->_key << endl;
- _InOrder(root->_right);
- }
-
- void InOrder()
- {
- _InOrder(_root);
- }
-
- void _Destory(Node* root)
- {
- if (root == nullptr)
- return;
- _Destory(root->_left);
- _Destory(root->_right);
- delete root;
- }
-
- ~AVLTree()
- {
- _Destory(_root);
- _root =nullptr;
- }
-
-
- int TreeHight(Node* root)
- {
- if (root == nullptr)
- return 0;
-
- int leftdepth = TreeHight(root->_left);
- int rightdepth = TreeHight(root->_right);
-
- //返回 左右子树 大的那个
- return leftdepth > rightdepth ? leftdepth + 1 : rightdepth + 1;
- }
-
- bool _isBalance(Node* root)
- {
- if (root == nullptr)
- return true; //空树也是平衡树
-
- int Maxleft = TreeHight(_root->_left);
- int Maxright = TreeHight(_root->_right);
-
- return abs(Maxleft - Maxright) < 2
- && _isBalance(root->_left)
- && _isBalance(root->_right);
- }
-
- bool _isBalance()
- {
- return _isBalance(_root);
- }
关于如何理解检查这棵树是否平衡的函数,也就不多赘述。因为在前面二叉树篇讲过。
下面就进入重头戏~

- //如果是第一次插入 直接去做根 root
- if (_root == nullptr)
- {
- _root = new Node(k);
- return true;
- }
-
- //不是第一个值
- Node* cur = _root;
- Node* parent = nullptr;
- while (cur) //这里和find的思想一样
- {
- if (key > cur->_key)
- {
- parent = cur;
- cur = cur->_right;
- }
- else if (key < cur->_key)
- {
- parent = cur;
- cur = cur->_left;
- }
- else
- {
- //如果走到这里 说明 已经插入了和key 一样的数
- return true;
- }
- }
-
- //cur 走到空的位置了
- cur = new Node(key);
- Node* newnode = cur;
-
- //更新与parent的关系
- if (cur->_key > parent->_key)
- {
- parent->_right = cur;
- }
- else
- {
- parent->_left = cur;
- }
- //注意我们设计的是三叉连 结构
- //cur 还有反过来 指向parent
- cur->_parent = parent;
任何一次对树数据的操作,都可能造成对树结构的破坏。
所以,为了 保护树的结构。我们需要根据不同情况,对树进行位置的更换
我们让在根节点 右插入对 _bf ++
左插入 _bf--;
由此,每棵树节点的 平衡因子会分为三种情况。
[0] [-1,1] [2,-2]

- //调节平衡因子;
- //while(parent)
- while (cur != root)
- {
- //更新节点bf
- if (cur == parent->_left)
- {
- parent->_bf--;
- }
- else
- {
- parent->_bf++;
- }
-
-
- //根据平衡因子 进行调整
- if (parent->_bf == 0)
- {
- break; //不做任何事情
- }
- else if(parent->_bf ==-1 || parent->_bf == 1)
- {
- //向上调整
- cur = parent;
- parent->_parent;
- }
- else if(parent->_bf == -2 || parent->_bf == 2)
- {
- //调整
- }
- else
- {
- //理论上不会走到这里来
- //如果走到这里来 也是调整前 已经不是AVL数
- assert(false);
- }
- }


我们在看单旋图中,就是bf=-1,1 的节点(也就是parent) 是旋转的轴。
- else if(parent->_bf == -2 || parent->_bf == 2)
- {
- //调整
- //左边新增节点
- if (parent->_bf == -2)
- {
- if (cur->_bf == -1) //新节点在左边插入
- {
- //左高 右边低 需要右翻转
- RotateR(parent);
- }
- else //新增节点插入在右边
- {
-
- }
- }
- else //右边新增节点
- {
- if (cur->_bf == 1) //新节点从右边插入
- {
- //此时 右高 左低 需要 右翻转
- RotateL(parent);
- }
- else //新增节点在左边
- {
-
- }
- }
- //翻转完 就跳出
- break;
- }
这也就是单旋插入的逻辑。
右单旋实现:

也及时sub subLR parent 三方的关系;
- void RotateR(Node* parent)
- {
- Node* sub = parent->_left;
- Node* subLR = sub->_right;
-
- //让subLR去做parent的左
- parent->_left = subLR;
- //subLR可能不存在
- if(subLR)
- subLR->_parent = parent;
-
- sub->_right = parent;
- //记录 父节点 因为 sub 可能不是唯一独立的树
- Node* grandparent = parent->_parent;
- parent->_parent = sub;
-
- if (parent == _root)
- {
- //独立树
- _root = sub;
- sub->_parent = nullptr;
- }
- else
- {
- //子树
- //parent的 父亲仍然保留着 节点地址
- if (grandparent->_left == parent)
- {
- grandparent->_left = sub;
- }
- else
- {
- grandparent->_right = sub;
- }
- //更新回来
- sub->_parent = grandparent;
- }
-
- //调节平衡因子
- parent->_bf = sub->_bf = 0;
- }
- RotateL(Node* parent)
- {
- Node* sub = parent->_right;
- Node* subRL = sub->_left;
-
- //让subRL 去做parent的右边
- parent->_right = subRL;
- //仍然注意避坑
- if(subRL)
- subRL->_parent = parent;
-
- Node* grandparent = parent->_parent;
- sub->_left = parent;
- parent->_parent = sub;
-
- if (parent == _root)
- {
- _root = sub;
- sub->_parent = nullptr;
- }
- else
- {
- if (grandparent->_left == parent)
- {
- grandparent->_left = sub;
- }
- else
- {
- grandparent->_right = sub;
- }
- sub->_parent = grandparent;
- }
-
- parent->_bf = sub->_bf = 0;
- }
左右双旋转:
- void RotateLR(Node* parent)
- {
- Node* sub = parent->_left;
- Node* subLR = sub->_right;
- //保存插入处 的平衡因子~
- int bf = subLR->_bf;
-
- RotateL(sub);
- RotateR(parent);
-
- //调整平衡因子
- if (bf == -1) //左边B插入
- {
- sub->_bf = 0;
- subRL->_bf = 0;
- parent->_bf = 1;
- }
- else if(bf ==1)
- {
- sub->_bf = -1;
- subRL->_bf = 0;
- parent->_bf = 0;
- }
- else if(bf ==0)
- {
- sub->_bf = 0;
- subRL->_bf = 0;
- parent->_bf = 0;
- }
- else
- {
- assert(false);
- }
- }
右左双旋:
这个同上也就 不再多分析。

- void RotateRL(Node* parent)
- {
- Node* sub = parent->_right;
- Node* subRL = sub->_left;
- int bf = subRL->_bf;
-
- RotateR(sub);
- RotateL(parent);
-
- if (bf == 1) //从C插入
- {
- subRL->_bf = 0;
- sub->_bf = 0;
- parent->_bf = -1;
- }
- else if (bf == -1)
- {
- parent->_bf = 0;
- subRL->_bf = 0;
- sub->_bf = 1;
- }
- else if (bf == 0)
- {
- parent->_bf = 0;
- subRL->_bf = 0;
- sub->_bf = 0;
- }
- else
- {
- assert(false);
- }
- }
插入也就到此为止,我们先来试着跑几组数据看是否正确。


- namespace dy
- {
- template<class K, class V>
- struct AVLTreeNode
- {
- //节点指针
- AVLTreeNode
* _left; - AVLTreeNode
* _right; - AVLTreeNode
* _parent; - //节点数值
- pair
_kv; -
- //为了调节平衡 这里引入平衡因子;
- int _bf;
-
- //构造
- AVLTreeNode(const pair
& kv) - :_left(nullptr),
- _right(nullptr),
- _parent(nullptr),
- _kv(kv),
- _bf(0) //任何新插入的 节点 平衡因子都默认设置为0
- {}
- };
-
- template<class K,class V>
- struct AVLTreeKV
- {
- typedef AVLTreeNode
Node; - Node* _root;
-
- AVLTreeKV()
- :_root(nullptr)
- {}
-
- void _InOrder(Node* root)
- {
- if (root == nullptr)
- return;
-
- _InOrder(root->_left);
- cout << root->_kv.first << ":"<
_kv.second< - _InOrder(root->_right);
- }
-
-
- //没封装迭代器 暂且用Node* 替
- pair
bool > insert(const pair kv) - {
- //如果是第一次插入 直接去做根 root
- if (_root == nullptr)
- {
- _root = new Node(kv);
- return make_pair(_root,true);
- }
-
- //不是第一个值
- Node* cur = _root;
- Node* parent = nullptr;
- while (cur) //这里和find的思想一样
- {
- if (kv.first > cur->_kv.first)
- {
- parent = cur;
- cur = cur->_right;
- }
- else if (kv.first < cur->_kv.first)
- {
- parent = cur;
- cur = cur->_left;
- }
- else
- {
- //如果走到这里 说明 已经插入了和key 一样的数
- return make_pair(cur,true);
- }
- }
-
- //cur 走到空的位置了
- cur = new Node(kv);
- Node* newnode = cur;
-
- //更新与parent的关系
- if (cur->_kv.first > parent->_kv.first)
- {
- parent->_right = cur;
- }
- else
- {
- parent->_left = cur;
- }
- //注意我们设计的是三叉连 结构
- //cur 还有反过来 指向parent
- cur->_parent = parent;
-
- //调节平衡因子;
- //while(parent)
- while (cur != _root)
- {
- //更新节点bf
- if (cur == parent->_left)
- {
- parent->_bf--;
- }
- else
- {
- parent->_bf++;
- }
-
-
- //根据平衡因子 进行调整
- if (parent->_bf == 0)
- {
- break; //不做任何事情
- }
- else if (parent->_bf == -1 || parent->_bf == 1)
- {
- //向上调整
- cur = parent;
- parent = parent->_parent;
- }
- else if (parent->_bf == -2 || parent->_bf == 2)
- {
- //调整
- //左边新增节点
- if (parent->_bf == -2)
- {
- if (cur->_bf == -1) //新节点在左边插入
- {
- //左高 右边低 需要右翻转
- RotateR(parent);
- }
- else //新增节点插入在右边
- {
- //此时 是折线
- //使用双旋
- RotateLR(parent);
- }
- }
- else //右边新增节点
- {
- if (cur->_bf == 1) //新节点从右边插入
- {
- //此时 右高 左低 需要 右翻转
- RotateL(parent);
- }
- else //新增节点在左边
- {
- RotateRL(parent);
- }
- }
- //翻转完 就跳出
- break;
- }
- else
- {
- //理论上不会走到这里来
- //如果走到这里来 也是调整前 已经不是AVL数
- assert(false);
- }
- }
- return make_pair(newnode,true);
- }
-
- }



总结:
①AVL树 插入节点时很看重平衡因子。
②平衡因子条件 [0] [-1,1] [-2,2]
③单旋是直线 双旋 为折线
内容到这里也就结束啦,感谢你的阅读。
祝你好运~
