AVL树的性质
BST树虽然可以缩短查找的效率,但如果数据有序或接近有序,BST树就会退化为接近线性的结构,查找元素相当于线性查找,效率低下;
AVL树本质上是一颗二叉搜索树,但是它又具有以下特点:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。下面是非平衡二叉搜索树和平衡二叉搜索树对比的例图:
实现AVL树
AVL树数据结构
结点的值以int型为例子,一个AVL树的结点有5个属性,分别是当前结点的值val
,左孩子结点left
,右孩子结点right
,父结点parent
,平衡因子bf
;
平衡因子bf
的值为左子树的高度减去右子树的高度,AVL树结点的平衡因子只能是-1、0、1。
struct node {
int val;
node* parent;
node* left;
node* right;
int bf;
node(int x = 0) :val(x), parent(nullptr), left(nullptr), right(nullptr),bf(0) {}
void show() { //中序遍历
if (this != nullptr) {
this->left->show();
cout << this->val << " ";
this->right->show();
}
}
};
class AVL {
node* root;
public:
AVL():root(nullptr){}
bool insert(int x);
void show() {
this->root->show();
cout << endl;
}
private:
void leftOne(node* p); //左单旋
void rightOne(node* p); //右单旋
void leftRight(node* p); //左右双旋
void rightLeft(node* p); //右左双旋
};
插入新结点
1)找到插入新节点的位置并连接
2)修改平衡因子并调整成AVL树
当AVL树插入新结点后,需要修改平衡因子,当插入节点是父节点的左子树时,父节点的平衡因子-1
,
当插入节点是父节点的右子树时,父节点的平衡因子1
;
修改完成后父节点的平衡因子有三种情况:
1)平衡因子等于0,插入完成;
2)平衡因子绝对值等于1,把插入节点更新为父节点,父节点跟新为父节点的父节点,继续往上调整平衡因子;
3)平衡因子绝对值等于2,树结构调整成AVL树结构,插入成功。
void insert(int x) {
if(root == nullptr){
root = new node(x);
return ;
}
node* newnode = new node(x);
node* p = this->root;
while (p) {
if (x < p->val) {
if (p->left == nullptr) {
p->left = newnode;
newnode->parent = p;
break;
}
p = p->left;
}
else {
if (p->right == nullptr) {
p->right = newnode;
newnode->parent = p;
break;
}
p = p->right;
}
}
node* _parent = p;
node* _child = newnode;
while (_parent) {
if (_parent->left == _child) {
_parent->bf--;
}
else {
_parent->bf++;
}
if (_parent->bf == 0) {
break; //平衡因子为0,插入完成
}
else if (_parent->bf == 1 || _parent->bf == -1) {
_child = _parent;//往上调整平衡因子
_parent = _parent->parent;
}
else if (_parent->bf == 2) {
//调整树结构
}
else if (_parent->bf == -2) {
//调整树结构
}
}
}
插入调整
调整树结构分很多种情况,具体用图说明。
右单旋和左单旋
右单旋
如下图,P、C结点为AVL树结点,ABD的结构是AVL树结构,h为数的高度,h>=0,当在子树A插入一个结点使A的高度增加1,结点P的平衡因子就变为-2,只需一次右单旋即可使其平衡。
对P结点进行右单旋过程:
1)保存C结点的地址,把P的左结点指向C的子树B,如果B不是空树,B的父结点指向P;
2)C的右结点指向P、C的父节点指向P的父结点,如果P的父结点是空,则把根结点指向C;如果不空,需要把原来指向P的结点指针指向C;
3)P的父节点指向C,更新平衡因子,子树A、B、D的结构并没有改变,不需要更改,C和P的左右子树高度一致,平衡因子都更新为0
void rightOne(node* p) {
node* c = p->left;
p->left = c->right;
if (p->left != nullptr) {
p->left->parent = p;
}
c->right = p;
c->parent = p->parent;
if (p->parent != nullptr) {
if (p->parent->left == p) {
p->parent->left = c;
}
else {
p->parent->right = c;
}
}
else {
root = c;
}
p->parent = c;
c->bf = 0;
p->bf = 0;
}
调整完成后如下图:
左单旋
如下图,P、C结点为AVL树结点,ABD的结构是AVL树结构,h为数的高度,h>=0,当在子树A插入一个结点使A的高度增加1,结点P的平衡因子就变为2,只需一次左单旋即可使其平衡。
左单旋跟右单旋步骤几乎一致,把代码中的left变right、right变left即可
void leftOne(node* p) {
node* c = p->right;
p->right = c->left;
if (p->right != nullptr) {
p->right->parent = p;
}
c->left = p;
c->parent = p->parent;
if (p->parent != nullptr) {
if (p->parent->left == p) {
p->parent->left = c;
}
else {
p->parent->right = c;
}
}
else {
root = c;
}
p->parent = c;
c->bf = 0;
p->bf = 0;
}
左右双旋和右左双旋
左右双旋
如下图中的左半部分,P、C、A是AVL结点,B、D子树平衡,高度为h,E、F子树平衡,高度为h-1,当在红色位置其中之一插入一个结点时,P的C的平衡因子为1,P的平衡因子为-2,需要两次旋转才能使其平衡;
右半部分插入节点A是左半部分的特例,平衡操作一致,但在更新指向时需要判空;
1)先保存旋转前A结点的平衡因子,为后面调整平衡因子做准备
2)对结点C左旋
3)再对结点P右旋
4)调整平衡因子
两次旋转过程中,子树B、E、F、D的结构并没有改变,平衡因子不变;对C结点左旋的时候,A、C结点的平衡因子修改为0,对P右旋的时候,P、C结点平衡因子修改为0;如果新节点的位置再E子树下,那么旋转前A结点的平衡因子为-1,调整后C的平衡因子为0,P结点的平衡因子为1;如果新节点的位置再F子树下,那么旋转前A结点的平衡因子为1,调整后P的平衡因子为0,C结点的平衡因子为-1;如果旋转前A的平衡因子为0,则不需要调整,因为两次旋转已经把他们的平衡因子置0了。
void leftRight(node* p) {
node* c = p->left;
int bf = c->right->bf;
leftOne(c);
rightOne(p);
if (bf == -1) {
p->bf = 1;
}
if (bf == 1) {
c->bf = -1;
}
}
右左双旋跟左右双旋思路是一样的,这里就不再赘述,直接上代码
void rightLeft(node* p) {
node* c = p->right;
int bf = c->left->bf;
rightOne(c);
leftOne(p);
if (bf == -1) {
p->bf = 1;
}
if (bf == 1) {
c->bf = -1;
}
}
AVL树的验证
#include <iostream>
#include <vector>
#include <time.h>
using namespace std;
struct node {
int val;
node* parent;
node* left;
node* right;
int bf;
node(int x = 0) :val(x), parent(nullptr), left(nullptr), right(nullptr),bf(0) {}
void show() {
if (this != nullptr) {
this->left->show();
cout << this->val << " ";
this->right->show();
}
}
};
class AVL {
node* root;
public:
AVL():root(nullptr){}
void insert(int x) {
if(root == nullptr){
root = new node(x);
return ;
}
node* newnode = new node(x);
node* p = this->root;
while (p) {
if (x < p->val) {
if (p->left == nullptr) {
p->left = newnode;
newnode->parent = p;
break;
}
p = p->left;
}
else {
if (p->right == nullptr) {
p->right = newnode;
newnode->parent = p;
break;
}
p = p->right;
}
}
node* _parent = p;
node* _child = newnode;
while (_parent) {
if (_parent->left == _child) {
_parent->bf--;
}
else {
_parent->bf++;
}
if (_parent->bf == 0) {
break;
}
else if (_parent->bf == 1 || _parent->bf == -1) {
_child = _parent;
_parent = _parent->parent;
}
else if (_parent->bf == 2) {
if (_parent->right->bf == 1) {
leftOne(_parent);
}
else if (_parent->right->bf == -1) {
rightLeft(_parent);
}
break;
}
else if (_parent->bf == -2) {
if (_parent->left->bf == -1) {
rightOne(_parent);
}
else if (_parent->left->bf == 1) {
leftRight(_parent);
}
break;
}
}
}
void show() {
this->root->show();
cout << endl;
}
private:
void leftOne(node* p) {
node* c = p->right;
p->right = c->left;
if (p->right != nullptr) {
p->right->parent = p;
}
c->left = p;
c->parent = p->parent;
if (p->parent != nullptr) {
if (p->parent->left == p) {
p->parent->left = c;
}
else {
p->parent->right = c;
}
}
else {
root = c;
}
p->parent = c;
c->bf = 0;
p->bf = 0;
}
void rightOne(node* p) {
node* c = p->left;
p->left = c->right;
if (p->left != nullptr) {
p->left->parent = p;
}
c->right = p;
c->parent = p->parent;
if (p->parent != nullptr) {
if (p->parent->left == p) {
p->parent->left = c;
}
else {
p->parent->right = c;
}
}
else {
root = c;
}
p->parent = c;
c->bf = 0;
p->bf = 0;
}
void leftRight(node* p) {
node* c = p->left;
int bf = c->right->bf;
leftOne(c);
rightOne(p);
if (bf == -1) {
p->bf = 1;
}
if (bf == 1) {
c->bf = -1;
}
}
void rightLeft(node* p) {
node* c = p->right;
int bf = c->left->bf;
rightOne(c);
leftOne(p);
if (bf == -1) {
p->bf = 1;
}
if (bf == 1) {
c->bf = -1;
}
}
};
void test(vector<int>& v) {
time_t t;
srand((unsigned)time(&t));
for (int i = 0; i < 100; i++) {
v.push_back(rand() % 100);
}
}
int main() {
AVL* t = new AVL;
vector<int> v;
test(v);
for (int i = 0; i < 100; i++) {
t->insert(v[i]);
}
t->show();
}
中序遍历结果: