什么是AVL树
AVL树简介
AVL树又称为高度平衡的二叉搜索树,目的在于尽量降低二叉树的高度,减少平均搜索长度。
满足二叉搜索树的性质
类比二叉搜索树,先将树的结构确定下来,下面处理满足AVL树独有的性质即可。
满足AVL树的性质
- 左子树和右子树高度差的绝对值不超过1。
- 树中的每一个左子树和每一个右子树都是AVL树。
- 每一个节点都有一个平衡因子,AVL树中任意节点的平衡因子可取值为-1,1,0 。
AVL树插入、删除、查找效率问题
一颗AVL树有N个节点,高度可以保持在log2N,插入、删除、查找的时间复杂度也是O(log2N).
AVL树的插入
插入一个元素,只需要找到该元素合适的位置,然后插入进去(不能插入相同元素),此时还只是满足二叉搜索树的性质,但是AVL树是二叉搜索树的升级版,所以还要保证树是平衡的。那就要求每次插入元素后检查是否会影响树的高度,即是否影响树的平衡性,此时插入完毕,如果影响树的高度就要做出调整。
平衡因子的更新规则
1、左子树插入,那么该节点对应的父亲节点的bf-=1;
2、右子树插入节点,该节点对应父亲节点的bf+=1;
更新后的情况作如下分析:
情况1:当插入一个节点后当前节点的父节点的平衡因子变为0,说明当前树是平衡的,不会对上层节点产生影响,所以结束更新直接返回。
情况2:假如插入节点之后该节点的父节点平衡因子变为1或者-1,此时说明之前父节点的平衡因子是0,那就破坏了平衡,那就会对上层节点的平衡性产生影响。此时就需要不断循环向上层更新,只要上层是1或者-1,循环就无法停下,直到父节点为根节点为止,才算插入完毕。
情况3:(循环向上层更新父节点出现的)如果父节点的平衡因子变成-2 或者2,那么就要进行旋转,更新节点的bf值,调整树的高度。
情况4:节点的平衡因子为-3 或者3,这种情况是不可能出现的,如果有说明原来的树就不是AVL树。我们要保证每个树再插入之前都是一颗AVL树。
bool Insert(const K& key){
if (_root == NULL){
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* father = NULL;
while (cur){
if (cur->_key == key){
return false;
}
else if (cur->_key < key){
father = cur;
cur = cur->_right;
}
else{
father = cur;
cur = cur->_left;
}
}
//此时说明已经确定要插入的位置
cur = new Node(key);
if (father->_key < cur->_key){
father->_right = cur;
cur->_parent = father;
}
else(father->_key > cur->_key){
father->_left = cur;
cur->_parent = father;
}
//插入完成后要记得更新平衡因子
while (father){
if (father->_left == cur){
father->_bf -= 1;
}
else (father->_right == cur){
father->_bf += 1;
}
//根据更新后的平衡因子判断树是否平衡,如果不平衡进行旋转调整
if (father->_bf == 0){
return true;
}
else if (father->_bf == -1 || father->_bf == 1){
cur = father;
father = father->_parent;
//此时还不确定是否是不平衡的,要进一步向上追溯看上面父节点的bf
//值是否为-2或者2,如果是就要旋转,如果不是就不需要旋转
}
else if (father->_bf == -2 || father->_bf == 2){
if (father->_bf == 2 && cur->_bf == 1){
RotateL(father);
return true;
}
if (father->_bf == -2 && cur->_bf == -1){
RotateR(father);
return true;
}
if (father->_bf == 2 && cur->_bf == -1){
RotateRL(father);
return true;
}
if (father->_bf == -2 && cur->_bf == 1){
RotateLR(father);
return true;
}
}
else{
cout << "平衡因子异常" << endl;
}
}
}
解决情况3的旋转方法实现(旋转的分类)
1、左单旋:parent->bf=2;subR->bf=1
思路:
1、将parent节点和subRL节点链接起来(parent->_right=subRL);
2、如果ppNode为空,那么_root=subR。
3、ppNode不为空时,判断parent是ppNode的左孩子还是右孩子,如果是左孩子,那么ppNode->_left=subR;反之ppNode->_right=subR。
4、最后一步将subR和parent连接起来(subR->_left=parent,parent->_parent=subR)。
void RotateL(Node* parent){
Node* SubR = parent->_right;
Node* SubRL = SubR->_left;
Node*PpNode = parent->_parent;
parent->_right = SubRL;
if (SubRL != NULL){
SubRL->_parent = parent;
}
if (PpNode == NULL){
_root = SubR;
SubR->_parent = PpNode;
}
else{
if (PpNode->_left == parent){
PpNode->_left = SubR;
SubR->_parent = PpNode;
}
else{
PpNode->_right = SubR;
SubR->_parent = PpNode;
}
}
SubR->_left = parent;
parent->_parent = SubR;
parent->_bf = 0;
SubR->_bf = 0;
}
右单旋:parent->bf=-2;subL->bf=-1
思路:
1、将subLR连到parent的左边(sub小于subLR 小于parent)。
2、记录父亲的上层节点ppNode。然后判断ppNode是否为空,是则说明父节点是根节点,只需要将根节点更新成subL即可。
3、ppNode不为空 ,此时只要直到原来的parent节点是ppNode节点的左边还是右边就可以确定subL是ppNode节点的左边还是右边。
4、最后一步就是将subL和parent连接起来,subL->_right=parent。
void RotateR(Node* parent){
Node* SubL = parent->_left;
Node* SubLR = SubL->_right;
Node* PpNode = parent->_parent;
parent->_left = SubLR;
if (SubLR){
SubLR->_parent = parent;
}
if (PpNode == NULL){
_root = SubL;
SubL->_parent = NULL;
}
else{
if (PpNode->_left == parent){
PpNode->_left = SubL;
}
else{
PpNode->_right = SubL;
}
SubL->_parent = PpNode;
}
SubL->_right = parent;
parent->_parent = SubL;
SubL->_bf = 0;
parent->_bf = 0;
}
左右双旋:parent->bf=-2;subL->bf=1
思路:
由于在subLR的右边插入了新节点,那么首先对以subLR为父节点进行左单旋(RotateL(subL)),完成左旋之后在以parent为父节点进行右单旋(RotateR(parent))。
在更新平衡因子时符合以下规律:
原来的subLR | 1(新插入节点在subLR的右边) | 0 | -1(新插入节点在subLR的左边) |
---|---|---|---|
更新后的parent | 0 | 0 | 1 |
更新后的subL | 1 | 0 | 0 |
更新后的subLR | 0 | 0 | 0 |
void RotateLR(Node* parent){
Node* SubL = parent->_left;
Node* SubLR = SubL->_right;
int bf = SubLR->_bf;
RotateL(SubL);
RotateR(parent);
//更新平衡因子
if (bf == 0){
parent->_bf = 0;
SubL->_bf = 0;
}
else if (bf == 1){
parent->_bf = 0;
SubL->_bf = -1;
}
else{
parent->_bf = 1;
SubL->_bf =0;
}
SubLR->_bf = 0;
}
右左双旋:parent->bf=2;subR->bf=-1;
思路:
由于在subRL的左边插入了新节点,那么首先对以subR为父节点进行右单旋(RotateR(subR)),完成左旋之后在以parent为父节点进行左单旋(RotateL(parent))。
在更新平衡因子时符合以下规律:
原来的subRL | 1(新插入节点在subRL的右边) | 0 | -1(新插入节点在subRL的左边) |
---|---|---|---|
更新后的parent | -1 | 0 | 0 |
更新后的subR | 0 | 0 | 1 |
更新后的subRL | 0 | 0 | 0 |
void RotateRL(Node* parent){
Node* SubR = parent->_right;
Node* SubRL = SubR->_left;
int bf = SubRL->_bf;
RotateR(subR);
RotateL(parent);
//更新平衡因子
if (bf == 0){
parent->_bf = 0;
SubR->_bf = 0;
SubRL->_bf = 0;
}
else if (bf == 1){
parent->_bf = -1;
SubR->_bf = 0;
SubRL->_bf = 0;
}
else if(bf==-1){
parent->_bf =0;
SubR->_bf = 1;
SubRL->_bf = 0;
}
else{
cout << "参数有误" << endl;
}
}
经典面试题——判断一棵树是否是AVL树
思路1:递归的判断左右子树是不是AVL树,同时判断某一个子树是否是AVL树的时候又得递归求一遍其左右子树的高度,通过高度差来判断对应子树是否是AVL树,这样一来时间复杂度就是O(N^2)
在你答出上面的思路时候,面试官常常会让优化时间复杂度!!!
优化思路1的时间复杂度为O(N):判断一棵树是否是AVL树,同时借用一个输出型参数height带出左右子树的高度,利用高度差的绝对值小于2,得出结论。
(不采用递归查看平衡因子的方式是由于有的时候平衡因子更新出错,导致误判)
bool IsAVLtree(){
int height = 0;
return _IsAVLtree(_root, height);
}
bool _IsAVLtree(Node* root, int& height){
if (root == NULL){
height = 0;
return true;
}
int lheight = 0;
if (_IsAVLtree(root->_left, lheight) == false){
return false;
}
int rheight = 0;
if (_IsAVLtree(root->_right, rheight) == false){
return false;
}
if (abs(lheight - rheight) !=root->_bf){
cout << "平衡因子有误\n";
}
height = lheight > rheight ? lheight + 1 : rheight + 1;
return abs(lheight - rheight) < 2;
}
完整代码:
#include<iostream>
using namespace std;
template<class K>
struct TreeNode{
public:
TreeNode(const K& key)
:_parent(NULL)
, _left(NULL)
, _right(NULL)
, _key(key)
, _bf(0)
{}
TreeNode<K>* _parent;
TreeNode<K>* _left;
TreeNode<K>* _right;
K _key;
int _bf;
};
template< class K >
class AVLTree{
public:
typedef TreeNode<K> Node;
AVLTree()
:_root(NULL)
{}
bool Insert(const K& key){
if (_root == NULL){
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* father = NULL;
while (cur){
if (cur->_key == key){
return false;
}
else if (cur->_key < key){
father = cur;
cur = cur->_right;
}
else{
father = cur;
cur = cur->_left;
}
}
//此时说明已经确定要插入的位置
cur = new Node(key);
if (father->_key < cur->_key){
father->_right = cur;
cur->_parent = father;
}
else(father->_key > cur->_key){
father->_left = cur;
cur->_parent = father;
}
//插入完成后要记得更新平衡因子
while (father){
if (father->_left == cur){
father->_bf -= 1;
}
else (father->_right == cur){
father->_bf += 1;
}
//根据更新后的平衡因子判断树是否平衡,如果不平衡进行旋转调整
if (father->_bf == 0){
return true;
}
else if (father->_bf == -1 || father->_bf == 1){
cur = father;
father = father->_parent;
//此时还不确定是否是不平衡的,要进一步向上追溯看上面父节点的bf
//值是否为-2或者2,如果是就要旋转,如果不是就不需要旋转
}
else if (father->_bf == -2 || father->_bf == 2){
if (father->_bf == 2 && cur->_bf == 1){
RotateL(father);
return true;
}
if (father->_bf == -2 && cur->_bf == -1){
RotateR(father);
return true;
}
if (father->_bf == 2 && cur->_bf == -1){
RotateRL(father);
return true;
}
if (father->_bf == -2 && cur->_bf == 1){
RotateLR(father);
return true;
}
}
else{
cout << "平衡因子异常" << endl;
}
}
}
//左单旋
void RotateL(Node* parent){
Node* SubR = parent->_right;
Node* SubRL = SubR->_left;
Node*PpNode = parent->_parent;
parent->_right = SubRL;
if (SubRL != NULL){
SubRL->_parent = parent;
}
if (PpNode == NULL){
_root = SubR;
SubR->_parent = PpNode;
}
else{
if (PpNode->_left == parent){
PpNode->_left = SubR;
SubR->_parent = PpNode;
}
else{
PpNode->_right = SubR;
SubR->_parent = PpNode;
}
}
SubR->_left = parent;
parent->_parent = SubR;
parent->_bf = 0;
SubR->_bf = 0;
}
//右单旋
void RotateR(Node* parent){
Node* SubL = parent->_left;
Node* SubLR = SubL->_right;
Node* PpNode = parent->_parent;
parent->_left = SubLR;
if (SubLR){
SubLR->_parent = parent;
}
if (PpNode == NULL){
_root = SubL;
SubL->_parent = NULL;
}
else{
if (PpNode->_left == parent){
PpNode->_left = SubL;
}
else{
PpNode->_right = SubL;
}
SubL->_parent = PpNode;
}
SubL->_right = parent;
parent->_parent = SubL;
SubL->_bf = 0;
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 == 0){
parent->_bf = 0;
SubL->_bf = 0;
}
else if (bf == 1){
parent->_bf = 0;
SubL->_bf = -1;
}
else{
parent->_bf = 1;
SubL->_bf =0;
}
SubLR->_bf = 0;
}
//右左双旋
void RotateRL(Node* parent){
Node* SubR = parent->_right;
Node* SubRL = SubR->_left;
int bf = SubRL->_bf;
RotateR(subR);
RotateL(parent);
//更新平衡因子
if (bf == 0){
parent->_bf = 0;
SubR->_bf = 0;
SubRL->_bf = 0;
}
else if (bf == 1){
parent->_bf = -1;
SubR->_bf = 0;
SubRL->_bf = 0;
}
else if(bf==-1){
parent->_bf =0;
SubR->_bf = 1;
SubRL->_bf = 0;
}
else{
cout << "参数有误" << endl;
}
}
void InOrder(){
_InOrder(_root);
cout <<endl;
}
void _InOrder(Node* root){
if (root == NULL){
return;
}
_InOrder(_root->_left);
cout << _root->_key << " ";
_InOrder(_root->_right);
}
void Destroy(){
if (_root == NULL){
return;
}
Destroy(_root->_left);
Destroy(_root->_right);
delete _root;
_root = NULL;
}
//经典面试题-----判断一棵树是否为平衡树
bool IsAVLtree(){
int height = 0;
return _IsAVLtree(_root, height);
}
bool _IsAVLtree(Node* root, int& height){
if (root == NULL){
height = 0;
return true;
}
int lheight = 0;
if (_IsAVLtree(root->_left, lheight) == false){
return false;
}
int rheight = 0;
if (_IsAVLtree(root->_right, rheight) == false){
return false;
}
if (abs(lheight - rheight) !=root->_bf){
cout << "平衡因子有误\n";
}
height = lheight > rheight ? lheight + 1 : rheight + 1;
return abs(lheight - rheight) < 2;
}
private:
Node* _root;
};
int main(){
AVLTree<int> tree;
tree.Insert(1);
tree.Insert(2);
tree.Insert(3);
tree.Insert(4);
tree.InOrder();
cout << tree.IsAVLtree() << endl;
system("pause");
return 0;
}