今天我们学习的是二叉搜索树的“大哥”,们是:AVL树
AVL树的概念:
AVL树全称为二叉平衡搜索树,它是通过旋转的方式将一个不平衡的二叉搜索树转化为一个平衡的二叉搜索树。首先我们先用代码把AVL树的结点定义出来:
template <class K,class V>
struct AVLTreeNode
{
pair<K, V> _kv;
AVLTreeNode<K, V>* _parent;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
int _bf;
AVLTreeNode(const pair<K, V>& kv)
: _kv(kv)
, _parent(nullptr)
, _left(nullptr)
, _right(nullptr)
, _bf(0)
{
}
};
结点定义出来了以后我们实现插入功能,AVL树的特性和二叉平衡树一样,遇到比结点小的插入到左边,比节点大的插入到右边。如果和结点内数据大小一样,则不插入:
template <class K,class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
bool insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//插入数据
cur = new Node(kv);
// 得判断cur插入它父亲的左边还是右边
if (parent->_kv.first > cur->_kv.first)
{
parent->_left = cur;
cur->_parent = parent;
}
else
{
parent->_right = cur;
cur->_parent = parent;
}
private:
Node* _root = nullptr;
};
接下来我们需要调节平衡因子,在调节平衡因子的时候,我们需要分析该二叉树的各种情况。
二叉树的旋转:
我们通俗易懂的来讲,旋转共分为四种情况,分别是:左单旋、右单选、左右单旋、右左单转。
每一种情况都对应着不同的平衡因子。
左单旋:
因为节点插入的情况有很多,我们可以用抽象图来表示n种情况:
所以发生左单旋的情况就是parent的平衡因子是2,右孩子的平衡因子是1。它的代码实现如下:
void RotateL(Node * parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
Node* ppNode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (ppNode == nullptr)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppNode->_right == parent)
{
ppNode->_right = subR;
subR->_parent = ppNode;
}
else
{
ppNode->_left = subR;
subR->_parent = ppNode;
}
}
//调整bf的值
subR->_bf = parent->_bf = 0;
}
右单旋:
它的实现和左单旋类似,parent的平衡因子是-2,右孩子的平衡因子是-1,代码实现如下:
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
Node* ppNode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (ppNode == nullptr)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
{
ppNode->_left = subL;
subL->_parent = ppNode;
}
else
{
ppNode->_right = subL;
subL->_parent = ppNode;
}
}
//更新平衡因子
subL->_bf = parent->_bf = 0;
}
总结:都需要定义一个ppNode,目的是把不平衡的部分旋转变成平衡后和上面结点(平衡的)进行连接。上面结点可能为空可能不为空,所以要分情况讨论。最后还要调整结点的平衡因子。
左右单旋:
左单旋和右单旋实际的情况都是左斜线和右斜线(都是直的)。而左右单选是向左突出的折线。
左右单旋是通过一个左单旋再通过一个右单旋实现的。但是这三个节点的平衡因子是需要分情况来讨论。因为当结点插入b下面或者插入c下面情况都是不同的,所以我们要定义一个bf,通过bf的值来调节平衡因子,变量代码实现如下:
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(subL);
RotateR(parent);
if (bf ==-1)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 1;
}
else if (bf == 1)
{
subL->_bf = -1;
subLR->_bf = 0;
parent->_bf = 0;
}
else if (bf == 0)
{
subL->_bf = 0;
subLR->_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)
{
subR->_bf = 1;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = -1;
}
else if (bf == 0)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
AVL树的总体代码:
四种旋转全部搞清楚以后,就可以实现整个AVL树了:
template <class K,class V>
struct AVLTreeNode
{
pair<K, V> _kv;
AVLTreeNode<K, V>* _parent;
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
int _bf;
AVLTreeNode(const pair<K, V>& kv)
: _kv(kv)
, _parent(nullptr)
, _left(nullptr)
, _right(nullptr)
, _bf(0)
{
}
};
template <class K,class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
bool insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//插入数据
cur = new Node(kv);
// 得判断cur插入它父亲的左边还是右边
if (parent->_kv.first > cur->_kv.first)
{
parent->_left = cur;
cur->_parent = parent;
}
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 = 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)
{
RotateRL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
else
{
assert(false);
}
break;
}
else
{
assert(false);
}
}
return true;
}
void RotateL(Node * parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
Node* ppNode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (ppNode == nullptr)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppNode->_right == parent)
{
ppNode->_right = subR;
subR->_parent = ppNode;
}
else
{
ppNode->_left = subR;
subR->_parent = ppNode;
}
}
//调整bf的值
subR->_bf = parent->_bf = 0;
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
Node* ppNode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (ppNode == nullptr)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (ppNode->_left == parent)
{
ppNode->_left = subL;
subL->_parent = ppNode;
}
else
{
ppNode->_right = subL;
subL->_parent = ppNode;
}
}
//更新平衡因子
subL->_bf = parent->_bf = 0;
}
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(subL);
RotateR(parent);
if (bf ==-1)
{
subL->_bf = 0;
subLR->_bf = 0;
parent->_bf = 1;
}
else if (bf == 1)
{
subL->_bf = -1;
subLR->_bf = 0;
parent->_bf = 0;
}
else if (bf == 0)
{
subL->_bf = 0;
subLR->_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)
{
subR->_bf = 1;
subRL->_bf = 0;
parent->_bf = 0;
}
else if (bf == 1)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = -1;
}
else if (bf == 0)
{
subR->_bf = 0;
subRL->_bf = 0;
parent->_bf = 0;
}
else
{
assert(false);
}
}
private:
Node* _root = nullptr;
};
AVL树平衡的检验:
检验AVL树最关键的就是左子树-右子树的差的绝对值是否小于等于1。我们可以先实现一个Height函数递归求出一棵树的高度:
int Height(Node* root)
{
if (root == nullptr)
{
return 0;
}
int left = Height(root->_left);
int right = Height(root->_right);
return (left > right) ? left + 1 : right + 1;
}
然后再通过一个递归函数,求出每棵子树的左右子树高度差进行平衡的判断:
bool IsBalance()
{
return _IsBalance(_root);
}
bool _IsBalance(Node* root)
{
if (root == nullptr)
{
return true;
}
int lheight = Height(root->_left);
int rheight = Height(root->_right);
if (rheight - lheight != root->_bf)
{
cout << "平衡因子异常:" <<root->_kv.first<< endl;
return false;
}
return abs(lheight - rheight) < 2 && _IsBalance(root->_left) && _IsBalance(root->_right);
}
到这里AVL树的实现就全部结束了,感谢阅读与支持!!