平衡树——Treap,Splay
二叉查找树(BST)的两条性质:
1.该节点的关键码不小于它的左子树中任意节点的关键码
2.该节点的关键码不大于它的右子树中任意节点的关键码
显然,二叉树的中序遍历是关键码单调递增排列的序列。
为避免越界,在BST中插入关键码为正无穷和负无穷的结点为初始情况。
基本操作:
1.检索
递归:
(1)找到结点,返回值
(2)未找到节点且无路可走(子结点为空),返回
(3)结点偏大,向左继续递归
(4)结点偏小,向右继续递归
ps:也可以用循环写,只是我习惯用深搜而已
2.插入
若发现关键值的结点不存在时,新建节点。
3.排名(Kth)
根据平衡树的性质,一个结点的排名即左子树大小+1。
4.前驱/后继
首先找到结点,根据平衡树的性质:
若找其前驱,先向左走一步(比结点小的左子树中找),随
后不断向右走直到叶节点,即为比结点小的数中最大的一个。
后既同理。
5.删除
首先,检索得到结点。
若p的子结点个数小于2,则直接删除p,令子结点直接代替其 位置,与p的父节点相连。
若p既有左子树又有右子树,则求出p的后继结点next。若next 没有左子树,直接删除next,并令next的右子树代替next的位置。 最后, 再让next节点代替p结点, 删除p即可。
Treap(单旋转)
下面是用treap写普通平衡树(BZOJ3224)的代码,代码几乎是李煜东的《算法竞赛进阶指南》上面的样子(最喜欢的一本书)。
#include <iostream>
#include <cstdlib>
using namespace std;
const int SIZE = 100005;
struct Treap{
int l , r;
int val , dat;
int size , cnt;
} a[SIZE];
int tot , n , root , INF = 0x7fffffff;
int New( int val ){
a[++tot].val = val;
a[tot].size = a[tot].cnt = 1;
a[tot].dat = rand();
return tot;
}
void Update( int p ){
a[p].size = a[a[p].l].size + a[a[p].r].size + a[p].cnt;
}
void Build(){
New( -INF ) , New( INF );
root = 1 , a[1].r = 2;
Update( root );
}
void zig( int &p ){
int q = a[p].l;
a[p].l = a[q].r , a[q].r = p , p = q;
Update( a[p].r ) , Update( p );
}
void zag( int &p ){
int q = a[p].r;
a[p].r = a[q].l , a[q].l = p , p = q;
Update( a[p].l ) , Update( p );
}
void Insert( int &p , int val ){
if( p == 0 ){
p = New( val );
return;
}
if( val == a[p].val ){
a[p].cnt++ , Update( p );
return;
}
if( val < a[p].val ){
Insert( a[p].l , val );
if( a[p].dat < a[a[p].l].dat ) zig( p );
} else{
Insert( a[p].r , val );
if( a[p].dat < a[a[p].r].dat ) zag( p );
}
Update( p );
}
void Remove( int &p , int val ){
if( p == 0 ) return;
if( val == a[p].val ){
if( a[p].cnt > 1 ){
a[p].cnt-- , Update( p );
return;
}
if( a[p].l || a[p].r ){
if( a[p].r == 0 || a[a[p].l].dat > a[a[p].r].dat )
zig( p ) , Remove( a[p].l , val );
else
zag( p ) , Remove( a[p].r , val );
Update( p );
} else
p = 0;
}
val < a[p].val ? Remove( a[p].l , val ) : Remove( a[p].r , val );
Update( p );
}
int GetRankByVal( int p , int val ){
if( p == 0 ) return 0;
if( a[p].val == val ) return a[a[p].l].size;
if( val < a[p].val ) return GetRankByVal( a[p].l , val );
return GetRankByVal( a[p].r , val ) + a[a[p].l].size + a[p].cnt;
}
int GetValByRank( int p , int rank ){
if( p == 0 ) return INF;
if( a[a[p].l].size >= rank ) return GetValByRank( a[p].l , rank );
if( a[a[p].l].size + a[p].cnt >= rank ) return a[p].val;
return GetValByRank( a[p].r , rank - a[a[p].l].size - a[p].cnt );
}
int GetPrev( int val ){
int ans = 1;
int p = root;
while( p ){
if( val == a[p].val ){
if( a[p].l > 0 ){
p = a[p].l;
while( a[p].r ) 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 a[ans].val;
}
int GetNext( int val ){
int ans = 2;
int p = root;
while( p ){
if( val == a[p].val ){
if( a[p].r > 0 ){
p = a[p].r;
while( a[p].l ) 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 a[ans].val;
}
int main(){
Build();
int n;
cin >> n;
while( n-- ){
int f , x;
cin >> f >> x;
switch( f ){
case 1:
Insert( root , x );
break;
case 2:
Remove( root , x );
break;
case 3:
cout << GetRankByVal( root , x ) << "\n";
break;
case 4:
cout << GetValByRank( root , x + 1 ) << "\n";
break;
case 5:
cout << GetPrev( x ) << "\n";
break;
case 6:
cout << GetNext( x ) << "\n";
break;
}
}
return 0;
}
splay(伸展树)
伸展树(Splay Tree),也叫分裂树,是一种二叉排序树,它能在O(log n)内完成插入、查找和删除操作。
概括的说其操作——把一个结点旋转到根。
概括的说其转法——如果结点是父结点的左子节点,则进行右旋;如果结点是父节点的右子节点,则进行左旋。
逐渐可以理解转的原理的。
#include <bits/stdc++.h>
using namespace std;
const int _ = 1e5 + 20;
int n , opt , x;
struct Splay{
int root , cnt , val[_] , ch[_][2] , siz[_] , sz[_] , par[_];
bool chk( int x ){
return ch[par[x]][1] == x;
}
void pushup( int x ){
if( !x ) return;
siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + sz[x];
//cout<<x<<' '<<siz[x]<<" ";
}
int newnode( int x ){
++cnt;
val[cnt] = x , sz[cnt] = siz[cnt] = 1;
ch[cnt][0] = ch[cnt][1] = 0;
return cnt;
}
void rotate( int x ){
int y = par[x] , z = par[y] , k = chk( x ) , w = ch[x][k ^ 1];
ch[y][k] = w , par[w] = y;
ch[z][chk( y )] = x , par[x] = z;
ch[x][k ^ 1] = y , par[y] = x;
pushup( y ) , pushup( x );
}
void splay( int x , int goal = 0 ){
while( par[x] != goal ){
int y = par[x] , z = par[y];
if( z != goal ){
if( chk( x ) == chk( y ) ) rotate( y );
else
rotate( x );
}
rotate( x );
}
if( !goal ) root = x;
}
void insert( int x ){
int cur = root , p = 0;
while( cur && x != val[cur] ) p = cur , cur = ch[cur][x > val[cur]];
if( cur ) ++sz[cur] , splay( cur );
else
cur = ch[p][x > val[p]] = newnode( x ) , par[cur] = p , splay( cur );
}
void find( int x ){
if( !root ) return;
int cur = root;
while( ch[cur][x > val[cur]] && x != val[cur] ) cur = ch[cur][x > val[cur]];
splay( cur );
}
int rank( int x ){
find( x );
return siz[ch[root][0]] + 1;
}
int kth( int k ){
int cur = root;
while( true ){
if( ch[cur][0] && k <= siz[ch[cur][0]] ) cur = ch[cur][0];
else if( k > siz[ch[cur][0]] + sz[cur] )
k -= siz[ch[cur][0]] + sz[cur] , cur = ch[cur][1];
else
return cur;
}
}
int pre( int x ){
find( x );
if( val[root] < x ) return root;
int cur = ch[root][0];
while( ch[cur][1] ) cur = ch[cur][1];
return cur;
}
int suc( int x ){
find( x );
if( val[root] > x ) return root;
int cur = ch[root][1];
while( ch[cur][0] ) cur = ch[cur][0];
return cur;
}
void remove( int x ){
int u = pre( x ) , v = suc( x );
//cout<<u<<' '<<v<<endl;
splay( u ) , splay( v , u );
int w = ch[v][0];
if( sz[w] > 1 ) --sz[w] , splay( w );
else
siz[0] = sz[0] = ch[v][0] = 0;
//pushup(v),pushup(u);
}
void maintain( int x ){
if( !ch[x][0] && !ch[x][1] ) siz[x] = sz[x];
if( ch[x][0] ) maintain( ch[x][0] );
if( ch[x][1] ) maintain( ch[x][1] );
pushup( x );
}
void print( int x ){
cout << x << ' ' << val[x] << ' ' << sz[x] << ' ' << siz[x] << ' ' << ch[x][0] << ' ' << ch[x][1] << ' ' << endl;
if( ch[x][0] ) print( ch[x][0] );
if( ch[x][1] ) print( ch[x][1] );
}
} T;
int main(){
T.insert( -0x3f3f3f3f );
T.insert( 0x3f3f3f3f );
cin >> n;
for( int i = 1; i <= n; i++ ){
cin >> opt >> x;
if( opt == 1 ) T.insert( x );
else if( opt == 2 )
T.remove( x );
else if( opt == 3 )
cout << T.rank( x ) - 1 << '\n';
else if( opt == 4 )
cout << T.val[T.kth( x + 1 )] << '\n';
else if( opt == 5 )
cout << T.val[T.pre( x )] << '\n';
else if( opt == 6 )
cout << T.val[T.suc( x )] << '\n';
}
return 0;
}

浙公网安备 33010602011771号