BST
给定一颗二叉树,树上的每个节点带有一个数值,称为节点的权,所谓的“BST性质”是指,对于树中任意一颗节点:
1. 该节点的权不小于它的左子树中任意节点的权;
2. 该节点的权不大于它的右子树中任意节点的权;
即:
val(lefttree) <= val(root) <= val(righttree)
显然,二叉查找树的中序遍历是一个权值单增的序列,笔者曾天真的以为,同样的节点构成不同的BST,中序序列会因树的形状而改变。然实则非也!
然而,需要注意的是,为了避免越界,减少边界情况的判定,笔者一般在BST中额外的插入一个正无穷的权和一个负无穷的权,仅由这两个节点构成的BST是一颗初始的空BST:
下面请欣赏有注释的friendly的代码:
#include<bits/stdc++.h>
#define oo INT_MAX
#define maxn 50000
#define _rep(i, a, b) for(int i = (a); i <= (b); ++i)
#define _for(i, a, b) for(int i = (a); i < (b) ;++i)
using namespace std;
struct bst
{
int l, r;//左右子节点在数组中的下标
int val;//权
}a[maxn];
int cnt, root;
int __new(int val) {
a[++cnt].val = val;
return cnt;
}
void __init__() {
__new(-oo), __new(oo);
root = 1, a[1].r = 2;
}
int __get(int p, int val) {//找val
if (p == 0) return 0;//检索失败
if (val == a[p].val) return p;
return val < a[p].val ? __get(a[p].l, val) : __get(a[p].r, val);
}
void __ins(int& p, int val) {//插入
if (p == 0) {
p = __new(val);
return;
}
if (val == a[p].val) return; //有就直接返回
if (val < a[p].val) __ins(a[p].l, val);
else __ins(a[p].r, val);
}
void __del(int val) {
int& p = root;
while (p)
{
if(val == a[p].val)break;
p = val < a[p].val ? a[p].l : a[p].r;
}
if (p == 0)return;//did not find it
if (a[p].l == 0) {//no left son
p = a[p].r; //右子树代替
}else if (a[p].r == 0) {// no right son
p = a[p].l;
} else {//having left son and right son,using the successive node
int next = a[p].r;
while(a[next].l > 0) next = a[next].l;
__del(a[next].val);//此时a[next]没有左子树了
a[next].l = a[p].l, a[next].r = a[p].r;
p = next;
}
}
int __nxt(int val) {//find the successive node
int ans = 2; // a[2].val == oo
int p = root;
while(p) {
if (a[p].val == val) {
if(a[p].r > 0) {//有右子树
p = a[p].r;
while(a[p].l > 0)p = a[p].l;
ans = p;
}
break;
}
if(a[p].val > val && a[p].val < a[ans].val) ans = p;
p = val < a[p].val ? a[p].l : a[p].r;
}
return ans;
}
int __pre(int val) {//find the precursor node
int ans = 1; //a[1] == -oo
int p = root;
while(p)
{
if(a[p].val == val) {
if(a[p].l > 0) {//有左子树
p = a[p].l;
while(a[p].r > 0)p = a[p].r;
ans = p;
}
break;
}
if(a[p].val < val && a[p].val > a[ans].val) ans = p;
p = val < a[p].val ? a[p].l : a[p].r;
}
return ans;
}
void zig(int& p) {//右旋,clockwise
int q = a[p].l;
a[p].l = a[q].r, a[q].r = p;
p = q;
}
void zag(int& p) {//左旋,anti-clockwise
int q = a[p].r;
a[p].r = a[q].l, a[q].l = p;
p = q;
}
void _LDR_(int p) {
if(a[p].l)_LDR_(a[p].l);
cout << a[p].val << " ";
if(a[p].r)_LDR_(a[p].r);
}
int main() {
__init__();
int num;
while(cin >> num && num)__ins(root, num);
_LDR_(root);
system("pause");
}
其实朴素的BST有个致命的缺点–当给定的序列是严格升序或者是降序,那么BST就会退化成一条链,部分操作(查,删,增)将会退化成log(n)。
那么我们的前人为了避免这个问题,就发明了一种旋转方法,能改变树的结构,主要有两种操作:
zig&zag
the upper zig(4) to below
the under zag(2) to above
合理的操作会使bst变得平衡,如何才算是合理的操作呢?
我们前人发现了一种思想–随机化
由此衍生出treap:
我是传送门