一.二叉搜索树
在懂平衡二叉搜索树之前,必须要懂得二叉搜索树。
其实二叉搜索树的概念很好理解,普通二叉树可以随便存储数据如图1
可见上图就是普通的二叉树,除了必须满足每个节点至多有两个孩子节点外,没有任何限制,数据可以随意存储。
而二叉搜索树就是在普通二叉树的基础上增加了一点要求:左孩子存储元素必须小于根节点元素,右孩子存储元素必须大于或等于根节点元素。(注:部分教材也称为二叉排序树)
所以按上述限制对图一进行调整后如下:
有读者可能会觉得自己心里想的和图二给出的结构并不一样,这很正常,因为二叉搜索树并不唯一,比如:
1 2 3 //二叉排序树A的插入序列
2 1 3 //二叉排序树B的插入序列
以上A,B树对应的结构如下:
由上述过程可以看出,不同的插入顺序形成的二叉搜索树完全不同,对于树A,如果持续从小到大插入元素,该树就会变成一个链表状的结构,而且查找效率会变成O(N),但对于树B,效率会变成O(logN),因为其查找特点与二分查找极其相似,每次查不到想要元素,就直接舍弃了大于(或小于)该元素的部分。所谓平衡二叉树,就是想办法把树进行调整,从而实现O(logN)效率的二叉查找树。
简易实现代码:
#include<iostream>
using namespace std;
struct Node {
int value; //保存结点数值
Node*lchild, *rchild;//左右孩子
};
void make_tree(int val, Node*&root) {
if (!root) {//当结点为空时,代表该位置为适当插入位置
root = new Node();
root->lchild = root->rchild = NULL;//创建结点,左右为空
root->value = val;
return;
}
if (val < root->value)make_tree(val, root->lchild);//比根节点值小,向左插入;
else make_tree(val, root->rchild);//大于等于根节点,向右插入。
}
int main() {
Node *root=NULL;//初始化根节点为空
int N;//结点个数
int tmp;
cin >> N;
for (int i = 0; i < N; i++) {
cin >> tmp;
make_tree(tmp, root);
}
return 0;
}
二.平衡二叉查找树
熟悉了上述二叉搜索树以后,如何进行调整才能成为AVL树?
首先需要解决的一个问题是,如何判定树需要调整。
平衡二叉树要求对于其任意节点,其左右子树高度相差的绝对值不大于1。
(上图中结点标注的左右孩子高度差,并非存储数值)
由上述定义可知,只有树2不是平衡二叉搜索树(AVL树),因为只有它存在左右孩子高度差绝对值大于1的结点。
还有一点读者需要知道,一定是插入了某一个结点导致整棵树变成了非AVL树,插入的结点改变了从它到根节点所有经过的结点是高度,所以容易想到,只需要把距离插入的点最近的不平衡点修改平衡,整棵树自然就平衡了。
(对于上图修改的即为-2的点)
-
修改为平衡二叉树的方式
平衡被破坏只有两种情况,平衡因子为2或者-2,两种情况分别对应左子树不平衡和右子树不平衡。
故两种情况完全对称,在此只详细解释值为2的情况。
对于值为二,可以广义化为下图:
蓝色点为可能存在的点,如1的右子树存在且0的左(或右)子树存在,2的右子树存在,并不会改变现有的结构(如下图)
当他们均为空时,就是我们上上图所列举的最简单的三个元素构成的不平衡情况。对于这种,我们只需要对不平衡的点(值为2的点)做右旋转,使得它继承左子树的右孩子,并让他作为左子树的右孩子。
然后调整被移动过的点的高度并重新计算平衡因子就可以获得平衡的树,这是右旋。
然而对于这种情况,即第一个不平衡点(第一个值为2的点)的左子树为-1,这意味着插入的结点在其右子树,这时候就需要对左子树先左旋,并旋转为左子树。如下图
由此一来变转化为了右旋的问题,再进行一次右旋即可解决。
同理为-2的情况无非是变成了在右边不平衡,原理相同。在此不做阐述,提供代码如下:
#include<vector>
#include<iostream>
#include<math.h>
#include<algorithm>
using namespace std;
struct Node {
int v, hight;
Node*left, *right;
};
Node*root;
int getHight(Node*root) {
if (!root)return 0;
return root->hight;
}
int getbalabceFactor(Node*N) {
return getHight(N->left) - getHight(N->right);
}
void updateHight(Node*&N) {
N->hight = max(getHight(N->left), getHight(N->right)) + 1;
}
void R(Node*&root) {
Node*tmp = root->left;
root->left = tmp->right;
tmp->right = root;
updateHight(root);
updateHight(tmp);
root = tmp;
}
void L(Node*&root) {
Node*tmp = root->right;
root->right = tmp->left;
tmp->left = root;
updateHight(root);
updateHight(tmp);
root = tmp;
}
void insert(int val,Node*&root) {
if (root == NULL) {
root = new Node();
root->left = root->right = NULL;
root->v = val;
root->hight = 1;
return;
}
else if (root->v > val) {
insert(val, root->left);
updateHight(root);
if (getbalabceFactor(root) == 2) {
if (getbalabceFactor(root->left) == 1) {
R(root);
}
else if (getbalabceFactor(root->left) == -1) {
L(root->left);
R(root);
}
}
}
else {
insert(val, root->right);
updateHight(root);
if (getbalabceFactor(root) == -2) {
if (getbalabceFactor(root->right) == -1) {
L(root);
}
else if (getbalabceFactor(root->right) == 1) {
R(root->right);
L(root);
}
}
}
}
int main() {
int N, tmp;
cin >> N;
for (int i = 0; i < N; i++) {
cin >> tmp;
insert(tmp, root);
}
int i = getbalabceFactor(root);
return 0;
}