平衡二叉搜索树的入门
前言
更新:忘了说这篇文章正施工中了。
更新:今天下午才知道我介绍的是HAVL,不是正统AVL……(不过HAVL更优秀啦
若发现本文有错误或不当之处, 希望能指出, 谢谢。
当年, 我还没有把二叉搜索树学好, 就会了平衡树……
本文的目的不是研究各种原理。
本文适合会使用二叉搜索树且想初步了解一下平衡树的OIer。
本文以AVL树的维持平衡方式的讲解为主要内容。
平衡树是一类在恶意数据下, 树高仍然保持优秀的二叉搜索树。
对于平衡树,我觉得可以称它们为小脑发达的二叉搜索树, 而不必觉得它们很犇很难。
平衡树什么时候需要“平衡”(rebalance)?当搜索树的结构改变的时候。
搜索树的结构什么时候会改变呢?当对它进行插入、删除操作的时候。
由此,为简化文本复杂度,本文只讨论插入时的平衡维护。
正文
树高
AVL用这个数据判断自己的平衡程度。
我的计算公式:height of Node is max(height of Node's left child, height of Node's right child) + 1
就当给知识不牢固的人复习一下。
平衡因子(Balance Factor)
左右子树的高度差。
一颗AVL树的任一节点的平衡因子的绝对值都不会超过1。
定义一种程序中普适的平衡因子的计算方法(如右子树高减左子树高),
不但可以判断树是否失衡, 还能判断哪个子树偏高。
旋转(rotate)操作
基础旋转操作包括:左旋、 右旋。
它们用于解决简单的失衡情况。
如果情况复杂,就要进行组合旋转操作。
一些简单却重要的事实
1.一个节点的插入过程中, 会经过一些节点, 且这个节点的插入所导致的
树高变化只可能会波及到它们(注意是可能)。
2.由于递归的性质,检查一个子树的平衡性时, 它的两个子树都是平衡的。
举例实现
1.节点的定义
struct Node{
int val, cnt, height;//val 代表节点值, cnt代表节点的创建次数
Node *lc, *rc;//即 left child, right child
Node(int val) {
this->val = val;
cnt = height = 1;
lc = rc = NULL;
}//构造函数
}*root;
2.插入函数的编写
展示插入函数之前, 先来简单了解一下它的逻辑, 不要管实现细节, 后文会说。
为了保持二叉搜索树的性质, 寻址、插入操作自然和二叉搜索树一样。
插入之后?
插入之后, 树的结构就改变了, 就要进行自平衡了。
这是插入函数
//插入一个权为x节点
Node* insert(Node* u, int x) {
if(u == NULL) return new Node(x);
if(u->val == x) u->cnt++;
else {
if(u->val < x) u->rc = insert(u->rc, x);
else u->lc = insert(u->lc, x);
}
u->height = max(get_h(u->lc), get_h(u->rc)) + 1;//标记
rebalance(u);//标记
return u;
}
在主函数里是这样调用的root = insert(root, val)
。
3.插入函数中涉及到的函数
在插入函数中标好了, 分别是get_h
、rebalance
。
get_h
用于快速判断树高,用此函数的原因是指向NULL的指针不方便进行操作。
然而用数组就没有这么麻烦。
//返回节点u的树高
inline int get_h(Node* u) {
if(u == NULL) return 0;
return u->height;
}
rebalance
通过旋转使树达到一种严格平衡的状态。
旋转的细节后续会补上, 网上都有吧?
函数体
// > 1 为左偏
Node* rebalance(Node* u) {
int f = getfactor(u);
// case 1
if(f > 1 && getfactor(u->lc) > 0) {
return rotateRight(u);
}
// case 2
if(f < -1 && getfactor(u->rc) < 0) {
return rotateLeft(u);
}
// case 3
if(f > 1 && getfactor(u->lc) < 0) {
u->lc = rotateLeft(u->lc);
return rotateRight(u);
}
// case 4
if(f < -1 && getfactor(u->rc) > 0) {
u->rc = rotateRight(u->rc);
return rotateLeft(u);
}
return u;
}
//简化版:
Node* ReBalance(Node* u) {
int f = Get_Factor(u);
// > 1 Left Greater
if(f > 1) {
if(Get_Factor(u->lc) < 0) u->lc = LeftRotate(u->lc);
return RightRotate(u);
}
if(f < -1) {
if(Get_Factor(u->rc) > 0) u->rc = RightRotate(u->rc);
return LeftRotate(u);
}
return u;
}
4.rebalance涉及到的函数
rotateRight 右旋
Node* rotateRight(Node* x) {
Node* y = x->lc;
x->lc = y->rc;
y->rc = x;
x->height = max(get_h(x->lc), get_h(x->rc)) + 1;
y->height = max(get_h(y->lc), get_h(y->rc)) + 1;
return y;
}
rotateLeft 左旋
Node* rotateLeft(Node* x) {
Node* y = x->rc;
x->rc = y->lc;
y->lc = x;
x->height = max(get_h(x->lc), get_h(x->rc)) + 1;
y->height = max(get_h(y->lc), get_h(y->rc)) + 1;
return y;
}
getfactor 获取一个节点的平衡因子,本题中,平衡因子以左树高减右树高计算。
inline int getfactor(Node* u) {
return get_h(u->lc) - get_h(u->rc);
}
效果测试
前言
树高 = 效果
源码
#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
struct Node{
int val, cnt, height;
Node *lc, *rc;
Node(int val) {
this->val = val;
cnt = height = 1;
lc = rc = NULL;
}
}*root;
inline int get_h(Node* u) {
if(u == NULL) return 0;
return u->height;
}
inline int getfactor(Node* u) {
return get_h(u->lc) - get_h(u->rc);
}
Node* rotateRight(Node* x) {
Node* y = x->lc;
x->lc = y->rc;
y->rc = x;
x->height = max(get_h(x->lc), get_h(x->rc)) + 1;
y->height = max(get_h(y->lc), get_h(y->rc)) + 1;
return y;
}
Node* rotateLeft(Node* x) {
Node* y = x->rc;
x->rc = y->lc;
y->lc = x;
x->height = max(get_h(x->lc), get_h(x->rc)) + 1;
y->height = max(get_h(y->lc), get_h(y->rc)) + 1;
return y;
}
// > 1 为左偏
Node* rebalance(Node* u) {
int f = getfactor(u);
// case 1
if(f > 1 && getfactor(u->lc) > 0) {
return rotateRight(u);
}
// case 2
if(f < -1 && getfactor(u->rc) < 0) {
return rotateLeft(u);
}
// case 3
if(f > 1 && getfactor(u->lc) < 0) {
u->lc = rotateLeft(u->lc);
return rotateRight(u);
}
// case 4
if(f < -1 && getfactor(u->rc) > 0) {
u->rc = rotateRight(u->rc);
return rotateLeft(u);
}
return u;
}
Node* insert(Node* u, int x) {
if(u == NULL) return new Node(x);
if(u->val == x) u->cnt++;
else {
if(u->val < x) u->rc = insert(u->rc, x);
else u->lc = insert(u->lc, x);
}
u->height = max(get_h(u->lc), get_h(u->rc)) + 1;
u = rebalance(u);
return u;
}
int main() {
int n, val;
freopen("test.txt", "r", stdin);
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
scanf("%d", &val);
root = insert(root, val);
}
cout << root->height;
fclose(stdin);
return 0;
}
恶意数据
数据生成器:
#include<bits/stdc++.h>
using namespace std;
int main() {
freopen("test.txt", "w", stdout);
srand((unsigned)time(0));
cout<<1000000<<'\n';
for(int i = 1; i <= 1000000; ++i) cout << i << '\n';
fclose(stdout);
return 0;
}
输出结果为整棵树的高度。
输出结果: 20
随机数据
数据生成器:
#include<bits/stdc++.h>
using namespace std;
int main() {
freopen("test.txt", "w", stdout);
srand((unsigned)time(0));
cout<<1000000<<'\n';
for(int i = 1; i <= 1000000; ++i) cout << rand()%1000000 + 1 << '\n';
fclose(stdout);
return 0;
}
输出结果:
第一次:18
第二次:18
第三次:18
第四次:18
第五次:18
第六次:18
第七次:18
第八次:18
第九次:18
第十次:18
保证数据随机。