浅析BST
do_while_true · · 题解
2020-11-12 update:修了一操作的锅
题目传送门
Q: 学习二叉搜索树有什么用?
A: 我们平常所说的"平衡树"(伸展树Splay,替罪羊树等)实际上都属于"平衡二叉搜索树",也就是既满足"平衡树"又满足"二叉搜索树"。二叉搜索树的效率比平衡二叉搜索树的效率低很多,但是在学习平衡二叉搜索树之前也要理解二叉搜索树的实现原理,此文就是来帮助理解的。
Q: 需要背过代码吗?
A: 不需要,相比背过二叉搜索树,不如多学一两个平衡树。
暴力BST最坏时间复杂度是
BST就是二叉搜索树,这里讲的是最普通的BST。
BST(Binary Search Tree),二叉搜索树,又叫二叉排序树
是一棵空树或具有以下几种性质的树:
-
若左子树不空,则左子树上所有结点的值均小于它的根结点的值
-
若右子树不空,则右子树上所有结点的值均大于它的根结点的值
-
左、右子树也分别为二叉排序树
-
没有权值相等的结点。
看到第4条,我们会有一个疑问,在数据中遇到多个相等的数该怎么办呢,显然我们可以多加一个计数器,就是当前这个值出现了几遍。
那么我们的每一个节点都包含以下几个信息:
-
当前节点的权值,也就是序列里的数
-
左孩子的下标和右孩子的下标,如果没有则为0
-
计数器,代表当前的值出现了几遍
-
子树大小和自己的大小的和
至于为什么要有4.我们放到后面讲。
节点是这样的:
struct node{
int val,ls,rs,cnt,siz;
}tree[500010];
其中
以下均以递归方式呈现。
插入:
#include<iostream>
#include<cstdio>
#define re register
using namespace std;
const int INF=0x7fffffff;
int cont;
struct node{
int val,siz,cnt,ls,rs;
}tree[1000010];
int n,opt,xx;
inline void add(int x,int v)
{
tree[x].siz++;
if(tree[x].val==v){
tree[x].cnt++;
return ;
}
if(tree[x].val>v){
if(tree[x].ls!=0)
add(tree[x].ls,v);
else{
cont++;
tree[cont].val=v;
tree[cont].siz=tree[cont].cnt=1;
tree[x].ls=cont;
}
}
else{
if(tree[x].rs!=0)
add(tree[x].rs,v);
else{
cont++;
tree[cont].val=v;
tree[cont].siz=tree[cont].cnt=1;
tree[x].rs=cont;
}
}
}
int queryfr(int x, int val, int ans) {
if (tree[x].val>=val)
{
if (tree[x].ls==0)
return ans;
else
return queryfr(tree[x].ls,val,ans);
}
else
{
if (tree[x].rs==0)
return tree[x].val;
return queryfr(tree[x].rs,val,tree[x].val);
}
}
int queryne(int x, int val, int ans) {
if (tree[x].val<=val)
{
if (tree[x].rs==0)
return ans;
else
return queryne(tree[x].rs,val,ans);
}
else
{
if (tree[x].ls==0)
return tree[x].val;
return queryne(tree[x].ls,val,tree[x].val);
}
}
int queryrk(int x,int rk)
{
if(x==0) return INF;
if(tree[tree[x].ls].siz>=rk)
return queryrk(tree[x].ls,rk);
if(tree[tree[x].ls].siz+tree[x].cnt>=rk)
return tree[x].val;
return queryrk(tree[x].rs,rk-tree[tree[x].ls].siz-tree[x].cnt);
}
int queryval(int x,int val)
{
if(x==0) return 0;
if(val==tree[x].val) return tree[tree[x].ls].siz;
if(val<tree[x].val) return queryval(tree[x].ls,val);
return queryval(tree[x].rs,val)+tree[tree[x].ls].siz+tree[x].cnt;
}
inline int read()
{
re int r=0;
re char ch=getchar();
while(ch<'0'||ch>'9')
ch=getchar();
while(ch>='0'&&ch<='9'){
r=(r<<3)+(r<<1)+(ch^48);
ch=getchar();
}
return r;
}
signed main()
{
n=read();
while(n--){
opt=read();xx=read();
if(opt==1) printf("%d\n",queryval(1,xx)+1);
else if(opt==2) printf("%d\n",queryrk(1,xx));
else if(opt==3) printf("%d\n",queryfr(1,xx,-INF));
else if(opt==4) printf("%d\n",queryne(1,xx,INF));
else{
if(cont==0){
cont++;
tree[cont].cnt=tree[cont].siz=1;
tree[cont].val=xx;
}
else add(1,xx);
}
}
return 0;
}
相信你已经掌握了二叉搜索树的基本实现方法,也可以来尝试循环实现的BST:
#include<iostream>
#include<cstdio>
#include<vector>
#define pb push_back
const int N = 10010;
const int INF = 0x7fffffff;
inline int read() {
int r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : w, ch = getchar();
while(ch >= '0' && ch <= '9') r = (r << 3) + (r << 1) + (ch ^ 48), ch = getchar();
return w ? ~r + 1 : r;
}
#define ls tree[x].son[0]
#define rs tree[x].son[1]
struct Node {
int val, siz, cnt, son[2];
}tree[N];
int n, root, tot;
inline void add(int v) {
if(!tot) {
root = ++tot;
tree[tot].cnt = tree[tot].siz = 1;
tree[tot].son[0] = tree[tot].son[1] = 0;
tree[tot].val = v;
return ;
}
int x = root, last = 0;
do {
++tree[x].siz;
if(tree[x].val == v) {
++tree[x].cnt;
break;
}
last = x;
x = tree[last].son[v > tree[last].val];
if(!x) {
tree[last].son[v > tree[last].val] = ++tot;
tree[tot].son[0] = tree[tot].son[1] = 0;
tree[tot].val = v;
tree[tot].cnt = tree[tot].siz = 1;
break;
}
} while(true);//Code by do_while_true qwq
}
int queryfr(int val) {
int x = root, ans = -INF;
do {
if(x == 0) return ans;
if(tree[x].val >= val) {
if(ls == 0) return ans;
x = ls;
}
else {
if(rs == 0) return tree[x].val;
ans = tree[x].val;
x = rs;
}
} while(true);
}
int queryne(int v) {
int x = root, ans = INF;
do {
if(x == 0) return ans;
if(tree[x].val <= v) {
if(rs == 0) return ans;
x = rs;
}
else {
if(ls == 0) return tree[x].val;
ans = tree[x].val;
x = ls;
}
} while(true);
}
int queryrk(int rk) {
int x = root;
do {
if(x == 0) return INF;
if(tree[ls].siz >= rk) x = ls;
else if(tree[ls].siz + tree[x].cnt >= rk) return tree[x].val;
else rk -= tree[ls].siz + tree[x].cnt, x = rs;
} while(true);
}
int queryval(int v) {
int x = root, ans = 0;
do {
if(x == 0) return ans;
if(tree[x].val == v) return ans + tree[ls].siz;
else if(tree[x].val > v) x = ls;
else ans += tree[ls].siz + tree[x].cnt, x = rs;
} while(true);
}
int main() {
n = read();
while(n--) {
int opt = read(), x = read();
if(opt == 1) printf("%d\n", queryval(x) + 1);
if(opt == 2) printf("%d\n", queryrk(x));
if(opt == 3) printf("%d\n", queryfr(x));
if(opt == 4) printf("%d\n", queryne(x));
if(opt == 5) add(x);
}
return 0;
}