看了很多人写的,除了少部分写得较好以外,其他写的乱七八糟
决定还是得自己写一篇以方便日后复习(特别的有关红黑树的)
ps:关于旋转,这里不说明(附有源码,看下即可,应该比较好理解),大概说一下核心点就是
通过旋转可以使一边子树的高度+1,另一边-1
比如左旋就是时右边高度-1,左边高度+1
AVL树的定义:
左右子树高度差不超过1的树因此我们只需要通过调整树的结构时其左右高度保存平衡即可
结构定义
template<class T> class AVLTreeNode { public: AVLTreeNode<T>* _left; AVLTreeNode<T>* _right; AVLTreeNode<T>* _parent; T _data; int _bf;//平衡因子 public: AVLTreeNode(const T& data) :_left(nullptr) , _right(nullptr) , _parent(nullptr) , _data(data) , _bf(0) {} };
我们通过平衡因子的方式去控制是否需要调整
左子树高度增加,则_bf--
右子树高度增加,则_bf++
所以在AVL树中,abs(_bf)<=1
//定义一个默认比较函数 template<class T> class cmp { public: bool operator()(const T& x, const T& y) { return x>y; } }; //考虑到V可能是pair类型,我们需要用K来查询 template<class K, class V, class KeyofT = cmp<K>> class AVLTree { typedef AVLTreeNode<V> Node; protected: //成员定义 Node* _head; //定义一个转换成key的方法,这样写一次就可以了 KeyofT cmp; public: //需要重写的默认生成函数 AVLTree() :_head(nullptr) {} ~AVLTree() {} public: //.....增删改的函数实现 };
基本结构框架
关于插入:
总的来说,我们只需关注这一件事(我这里在删除时的大思想也是类似的)
假设cur是新增节点,parent是它的父节点
那么parent在cur这一子树方向上是多了一个节点的
即我们需要改变parent的平衡因子
为了方便讨论,这里只说明cur是parent的左子树的情况(右子树完全类似)
情况1:如果parent的平衡因子原本为0,则插入后parent变-1
以parent为根的树整体高度增加,需要继续向上调整
情况2:如果parent的平衡因子原本为1,则插入后parent变为0
以parent为根的树左右两边已经平衡,由于总高度没有增加,所以不用继续向上调整
情况3:如果parent的平衡因子原本为-1,则插入后parent变为-2
此时该树的结构已经不平衡,需要进行旋转处理(这种情况是通过情况1演变过来的)旋转分为4种情况
第一种:
parent->_bf==-2&&cur->_bf==-1此时单纯左边高
我们只需进行右旋即可
第二种:
parent->_bf==2&&cur->_bf==1此时单纯右边高
我们只需要进行左旋即可
第三种:
parent->_bf==-2&&cur->_bf==1
此时parent的左边高,cur是右边高
我们需要先将cur进行左旋,变成都是左边高
再整体右旋
第四种:
parent->_bf==2&&cur->_bf==-1此时parent的右边高,cur是左高
我们需要先将cur进行右旋,变成都是右边高,
再整体左旋
第三种跟第四种,简单理解就是要变成第一种或第二种情况处理
简单画个图方便理解
对于平衡因子的调整,建议画图自行分析,看一下是怎么旋转的就可知道_bf是怎么调整的了
插入函数
bool insert(const V& data) { if (_head == nullptr) { //头结点为空 _head = new Node(data); return true; } Node* parent = nullptr; Node* cur = _head; while (cur) { if (cmp(cur->_data, data)) { parent = cur; cur = cur->_left; } else if (cmp(data, cur->_data)) { parent = cur; cur = cur->_right; } else { //相等,这里不考虑重复值,因此不进行插入 return false; } } //创建节点 cur = new Node(data); if (cmp(parent->_data, data)) { parent->_left = cur; } else { parent->_right = cur; } cur->_parent = parent; //由于高度发生变化,需要继续向上调整 //调整平衡 while (parent) { if (parent->_left == cur) { //说明左子树多了个节点 parent->_bf--; } else { //说明右子树多了个节点 parent->_bf++; } if (parent->_bf == 0) { //说明之前左右存在高度查,现在左右平衡了,总体高度没有增加,可以break了 break; } if (abs(parent->_bf) == 1) { //说明以parent为根节点的子树有一边高度增加了,需要继续向上调整 cur = parent; parent = cur->_parent; continue; } if (abs(parent->_bf) == 2) { //说明此时已经不平衡,需要进行旋转调整处理 if (parent->_bf == 2 && cur->_bf == 1) { //parent,cur均是右边高,进行左单旋即可 RotateL(parent); //旋转后修正平衡因子 parent->_bf = cur->_bf = 0; } else if (parent->_bf == -2 && cur->_bf == -1) {