首先是从二叉搜索树开始,一棵二叉搜索树的定义是:

1.这是一棵二叉树;

2.令x为二叉树中某个结点上表示的值,那么其左子树上所有结点的值都要不大于x,其右子树上所有结点的值都要不小于x。

由二叉搜索树的第二条定义,可以很方便地利用这种特点在二叉树中以O(logn)的渐进复杂度完成插入、查找、删除等操作。

但是这里还是有个问题,就是弄不好的话,一棵普通的二叉搜索树经过多次修改操作之后可能会导致整棵树左右“不平衡”,出现一边结点很多,另一边结点很少的情况,这样最终每一次的操作时间就会大大偏离O(logn),甚至最后退化成一条链。

平衡树就是通过一系列的操作,不断动态维持二叉搜索树的左右平衡,始终保证二叉搜索树的效率。

SBT全称Size Balanced Tree,是一种通过记录以每个结点为根的子树中的结点的个数size,然后进行二叉搜索树结构调整的方法,由OI选手陈启峰发明。

SBT除了一般的二叉搜索树性质之外,还有两条性质:

对于SBT中的每一个结点t,有:

1.s[right[t]]>=s[left[left[t]]],s[right[left[t]]];

2.s[left[t]]>=s[left[right[t]]],s[right[right[t]]].

即在上图中:

s[L]>=s[C],s[D];s[R]>=s[A],s[B].

这一性质能够保证整棵SBT能够在整体的结构上保持相对平衡,而为了保持这一额外的性质,需要通过不断的调整来完成。

调整即通过结点的左右旋完成,左右旋不改变二叉搜索树的性质,而能够调整SBT的size,使得整棵树能够始终保持为一棵SBT树。

  • 保持-maintain

这是SBT的核心操作,保持过程通过左右旋来完成。

  • 插入-insert

与普通的二叉搜索树相同,只是在插入完之后加入一个maintain操作。

  • 删除-del

与普通的二叉搜索树相同。删除完一个结点之后虽然不能够保证此时的树还能够严格保持SBT性质,但是树结构从整体上来说最大深度不会增加,因此maintain操作就不需要进行了。

  • 查找值-search

与普通的二叉搜索树相同。

  • 查找第k个-select

由于size的存在,SBT天生就很适合处理查找第k个数这样的任务。不断确定一下左子树和右子树的size即可,若某结点左子树的size+1=k,那么该结点就是第k个数。

  • 查找前趋/后继-pred/succ

与普通的二叉搜索树相同。

经过实际情况的分析,SBT中由于左右旋操作的存在,并不能严格保证与当前节点值相等的是在左边还是右边!!!因而应该不能够按照普通二叉搜索树的写法写......具体应该怎么写我还没有想到比较合适的方法(唉...囧...找了不少资料也基本上对这个都没怎么讲清楚)。下面贴的模板代码的pred和succ应该都是不合适的。

  • 获取最大/最小值-getmax/getmin

与普通的二叉搜索树相同。向左或者向右走到底即可。

模板如下:

 /* ***********************************************
MYID : Chen Fan
LANG : G++
PROG : Size Balanced Tree
************************************************ */ #include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm> using namespace std; #define MAXN 1000 typedef struct sbtnod
{
int key,left,right,size;
} sbtnode;
int sbttail,sbt; sbtnode tree[MAXN]; void rrotate(int& t)
{
int k=tree[t].left;
if (!k) return ;
tree[t].left=tree[k].right;
tree[k].right=t;
tree[k].size=tree[t].size;
tree[t].size=tree[tree[t].left].size+tree[tree[t].right].size+;
t=k;
} void lrotate(int& t)
{
int k=tree[t].right;
if (!k) return ;
tree[t].right=tree[k].left;
tree[k].left=t;
tree[k].size=tree[t].size;
tree[t].size=tree[tree[t].left].size+tree[tree[t].right].size+;
t=k;
} void maintain(int& t,bool flag)
{
if (!t) return ;
if (!flag)
if (tree[tree[tree[t].left].left].size>tree[tree[t].right].size) rrotate(t);
else if (tree[tree[tree[t].left].right].size>tree[tree[t].right].size)
{
lrotate(tree[t].left);
rrotate(t);
} else return ;
else
if (tree[tree[tree[t].right].right].size>tree[tree[t].left].size) lrotate(t);
else if (tree[tree[tree[t].right].left].size>tree[tree[t].left].size)
{
rrotate(tree[t].right);
lrotate(t);
} else return ; maintain(tree[t].left,false);
maintain(tree[t].right,true);
maintain(t,false);
maintain(t,true);
} void insert(int& t,int v)
{
if (!t)
{
sbttail++;
tree[sbttail].key=v;
tree[sbttail].size=;
t=sbttail;
} else
{
tree[t].size++;
if (v<tree[t].key) insert(tree[t].left,v);
else insert(tree[t].right,v);
maintain(t,v>=tree[t].key);
}
} int del(int& t,int v)
{
int ret;
tree[t].size--;
if (v==tree[t].key||(v<tree[t].key&&tree[t].left==)||(v>tree[t].key&&tree[t].right==))
{
ret=tree[t].key;
if (tree[t].left==||tree[t].right==) t=tree[t].left+tree[t].right;//
else tree[t].key=del(tree[t].left,tree[t].key+);
} else
{
if (v<tree[t].key) ret=del(tree[t].left,v);
else ret=del(tree[t].right,v);
}
return ret;
} int select(int t,int k)
{
if (k==tree[tree[t].left].size+) return t;
if (k<=tree[tree[t].left].size) return select(tree[t].left,k);
else return select(tree[t].right,k--tree[tree[t].left].size);
} int search(int t,int x)
{
if (t==||x==tree[t].key) return t;
if (x<tree[t].key) return search(tree[t].left,x);
else return search(tree[t].right,x);
} int getmin(int t)
{
int i=t;
while(tree[i].left)
{
i=tree[i].left;
}
return i;
} int getmax(int t)
{
int i=t;
while(tree[i].right)
{
i=tree[i].right;
}
return i;
} int pred(int& t,int y,int v)
{
if (!t) return y;
if (tree[t].key<v) return pred(tree[t].right,t,v);
else return pred(tree[t].left,y,v);
} int succ(int& t,int y,int v)
{
if (!t) return y;
if (tree[t].key>=v) return succ(tree[t].left,t,v);
else return succ(tree[t].right,y,v);
} void deb_out()
{
printf("-------\n");
printf("sbttail=%d sbt=%d\n",sbttail,sbt);
for (int i=;i<=sbttail;i++)
printf("%2d: key=%2d size=%2d left=%2d right=%2d\n",i,tree[i].key,tree[i].size,tree[i].left,tree[i].right);
printf("-------\n");
} int main()
{
memset(tree,,sizeof(tree));
sbttail=;
sbt=; for (int i=;i<=;i++) insert(sbt,i);
deb_out(); //printf("%d\n",del(sbt,8));
insert(sbt,);
deb_out();
del(sbt,);
del(sbt,);
printf("%d\n",search(sbt,));
deb_out();
}

Size Balanced Tree(SBT) 模板的更多相关文章

  1. Size Balanced Tree(SBT树)整理

    不想用treap和Splay,那就用SB树把,哈哈,其实它一点也SB,厉害着呢. 先膜拜一下作者陈启峰.Orz 以下内容由我搜集整理得来. 一.BST及其局限性 二叉查找树(Binary Search ...

  2. Size Balanced Tree

    Size Balanced Tree(SBT)是目前速度最快的平衡二叉搜索树,且能够进行多种搜索操作,区间操作:和AVL.红黑树.伸展树.Treap类似,SBT也是通过对节点的旋转来维持树的平衡,而相 ...

  3. C基础 - 终结 Size Balanced Tree

    引言 - 初识 Size Balanced Tree 最近在抽细碎的时间看和学习 random 的 randnet 小型网络库. iamrandom/randnet - https://github. ...

  4. 初学 Size Balanced Tree(bzoj3224 tyvj1728 普通平衡树)

    SBT(Size Balance Tree), 即一种通过子树大小(size)保持平衡的BST SBT的基本性质是:每个节点的size大小必须大于等于其兄弟的儿子的size大小: 当我们插入或者删除一 ...

  5. 子树大小平衡树(Size Balanced Tree,SBT)操作模板及杂谈

    基础知识(包括但不限于:二叉查找树是啥,SBT又是啥反正又不能吃,平衡树怎么旋转,等等)在这里就不(lan)予(de)赘(duo)述(xie)了. 先贴代码(数组模拟): int seed; int ...

  6. Size Balanced Tree(节点大小平衡树)

    定义 SBT也是一种自平衡二叉查找树,它的平衡原理是每棵树的大小不小于其兄弟树的子树的大小 即size(x->l)$\ge$size(x->r->l),size(x->r-&g ...

  7. Size Balance Tree(SBT模板整理)

    /* * tree[x].left 表示以 x 为节点的左儿子 * tree[x].right 表示以 x 为节点的右儿子 * tree[x].size 表示以 x 为根的节点的个数(大小) */ s ...

  8. 56. 2种方法判断二叉树是不是平衡二叉树[is balanced tree]

    [本文链接] http://www.cnblogs.com/hellogiser/p/is-balanced-tree.html [题目] 输入一棵二叉树的根结点,判断该树是不是平衡二叉树.如果某二叉 ...

  9. Link-Cut Tree指针模板

    模板: 以下为弹飞绵羊代码: #define Troy #include "bits/stdc++.h" using namespace std; ; inline int rea ...

随机推荐

  1. shell脚本中$

    变量名只能包含数字.字母和下划线,因为某些包含其他字符的变量有特殊含义,这样的变量被称为特殊变量. 例如,$ 表示当前Shell进程的ID,即pid ./package/kernel/mac80211 ...

  2. 开发MOSS自定义字段类型

    前段时间,由于刚好项目定制的需要,笔者就开发了几个自定义字段类型.在这抽空做个详细笔记,方便初学者学习.这方面的资料也很多,如果自身觉得不大明白可以参考下SDK和网上的相关文章.本章的目的主要是给新手 ...

  3. svg笔记----------path篇

    每个路径都必须以moveto 命令开始 moveto.lineto和closepath <path d="M 10 10 L 100 10z"/> 大写字母命令的坐标是 ...

  4. PHP:var_dump展示不全

    问题:var_dump展示不全,出现省略号 我们使用php的var_dump函数,可以更直观的查看输出结果,包括类型,数量等详细信息.此函数显示关于一个或多个表达式的结构信息,包括表达式的类型与值.数 ...

  5. GitHub这么火,程序员你不学学吗? 超简单入门教程 【转载】

    本GitHub教程旨在能够帮助大家快速入门学习使用GitHub. 本文章由做全栈攻城狮-写代码也要读书,爱全栈,更爱生活.原创.如有转载,请注明出处. GitHub是什么? GitHub首先是个分布式 ...

  6. MySQL整理碎片

    1 innodb引擎表 alter table TABLE_NAME engine='innodb'; 还有一种方法 optiize table TABLE_NAME; http://stackove ...

  7. c语言库函数

    #include <stdio.h> #define LENGTH 100 main() { FILE *fd; char str[LENGTH]; fd = fopen("he ...

  8. java中对List中对象排序实现

    package com.test; import java.util.ArrayList; import java.util.Collections; import java.util.Compara ...

  9. OpenGL网络资源

    转 十大OpenGL教程 1.http://nehe.gamedev.net/这个是我觉得全世界最知名的OpenGL教程,而且有网友将其中48个教程翻译成了中文http://www.owlei.com ...

  10. LightOJ 1259 Goldbach`s Conjecture 素数打表

    题目大意:求讲一个整数n分解为两个素数的方案数. 题目思路:素数打表,后遍历 1-n/2,寻找方案数,需要注意的是:C/C++中 bool类型占用一个字节,int类型占用4个字节,在素数打表中采用bo ...