二叉搜索树(Binary Search Tree)它要么是一棵空树,要么是一棵具有下列性质的二叉树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左,右子树也分别为二叉搜索树
二叉搜索树上的每一个结点都维护四个值,一个是它本身的权值data,有siz表示当前点为根的子树大小,ls记录左儿子的编号,rs记录右儿子的编号
1. 插入节点(insert)
插入节点的时候从根节点出发
(1) 如果当前节点为空,则将data赋为我们要插入的节点,siz初始化为1,再将其父亲连向他(这里我们要注意,二叉搜索树,是不记录父节点的,为了能使其父亲连向他,我们每次调用insert函数的时候我们传入其父亲指向其子节点的地址,而我们要使其父亲指向它,就是将该地址上的值改为新节点的编号)
(2) 如果要插入的节点比我们当前的节点小,就向该节点的左子树前进,同时,将该节点的siz加1(因为要插入的节点一定是插入到该节点的子树中去了)
(3) 如果要插入的节点比我们当前的节点大等于我们当前的节点,就向该节点的右子树前进,同时,将该节点的siz加1(因为要插入的节点一定是插入到该节点的子树中去了)
是不是很容易呀,实现如下:
void insert(int &now,int v) { if(!now) { data[++tot]=v; siz[tot]=1; now=tot; return; } ++siz[now]; if(data[now]<=v) insert(rs[now],v); else insert(ls[now],v); }
2. 删除节点(del)
其实删除操作非常奇怪,我们不可以直接删除一个有两个子节点的节点,我们从根节点出发
(1) 如果当前节点为空就返回
(2) 如果要插入的节点比我们当前的节点小,就向该节点的左子树前进,同时,将该节点的siz加1(因为要插入的节点一定是插入到该节点的子树中去了)
(3) 如果要插入的节点比我们当前的节点大,就向该节点的右子树前进,同时,将该节点的siz加1(因为要插入的节点一定是插入到该节点的子树中去了)
(4) 如果要插入的节点等于我们当前的节点
a) 如果该点只有一个儿子,就直接将该点的父亲连向该点的儿子,连接方法如上
b) 不然,我们就找到比该点大的最小点,将两点交换,再删除比该点大的最小点
是不是很容易呀,实现如下:
void del(int &now,int v) { if(!now) return; --siz[now]; if(data[now]==v) { if(!ls[now]||!rs[now]) now=ls[now]+rs[now]; else { int temp=rs[now]; while(ls[temp]) temp=ls[temp]; data[now]=data[temp]; del(rs[now],data[temp]); } } else if(data[now]<=v) del(rs[now],v); else del(ls[now],v); }
3. 查询某数排名(rank)
我们从根节点出发
(1) 如果当前节点为空就返回1
(2) 如果要插入的节点比我们当前的节点小,就向该节点的左子树继续查询
(3) 如果要插入的节点比我们当前的节点大,就向该节点的右子树继续查询,同时将查询得到的值+siz+1后返回
其实很容易写错,实现如下:
int rank(int &now,int v) { if(!now) return 1; if(data[now]<v) return siz[ls[now]]+1+rank(rs[now],v); else return rank(ls[now],v); }
4. 查询第k大的数(kth)
我们从根节点出发
(1) 如果当前节点为空就返回-1(找不到)
(2) 如果要查的排名等于siz[ls[now]]+1,就返回该节点
(3) 如果要插入的节点比我们当前的节点小,就向该节点的左子树继续查询
(4) 如果要插入的节点比我们当前的节点大,就向该节点的右子树继续查询排名为k-siz-1的数
其实很容易写错,实现如下:
int kth(int &now,int v) { if(!now) return -1; if(siz[ls[now]]+1==v) return data[now]; else if(siz[ls[now]]<v) return kth(rs[now],v-1-siz[ls[now]]); else return kth(ls[now],v); }
5. 求某数的前驱(前驱定义为小于该数,且最大的数)(pred)
我们从根节点出发
(1) 如果当前节点为空就返回-0x3f3f3f3f
(2) 如果要插入的节点比我们当前的节点小或等于我们当前的节点,就向该节点的左子树继续查询
(3) 如果要插入的节点比我们当前的节点大,就向该节点的右子树继续查询,并将查询得到的值与该点取最大值返回
其实很容易写错,实现如下
int pred(int &now,int v) { if(!now) return -0x3f3f3f3f; if(data[now]<v) return max(data[now],pred(rs[now],v)); else return pred(ls[now],v); }
6. 求某数的后继(后继定义为大于该数,且最小的数)(succ)
我们从根节点出发
(1) 如果当前节点为空就返回0x3f3f3f3f
(2) 如果要插入的节点比我们当前的节点小,就向该节点的左子树继续查询,并将查询得到的值与该点取最小值返回
(3) 如果要插入的节点比我们当前的节点大或等于我们当前的节点,就向该节点的右子树继续查询
其实很容易写错,实现如下:
int succ(int &now,int v) { if(!now) return 0x3f3f3f3f; if(data[now]<=v) return succ(rs[now],v); else return min(data[now],succ(ls[now],v)); }
是不是很容易呀,完整代码如下:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <map> #include <cstdlib> #include <algorithm> #include <queue> #include <stack> using namespace std; inline int read() { int a=0,q=0; char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); if(ch=='-') q=1,ch=getchar(); while(ch>='0'&&ch<='9') a=(a<<3)+(a<<1)+ch-48,ch=getchar(); return q?-a:a; } const int N=100100; int tot(0),root(0),siz[N],ls[N],rs[N],data[N],op,x,n; void insert(int &now,int v) { if(!now) { data[++tot]=v; siz[tot]=1; now=tot; return; } ++siz[now]; if(data[now]<=v) insert(rs[now],v); else insert(ls[now],v); } void del(int &now,int v) { if(!now) return; --siz[now]; if(data[now]==v) { if(!ls[now]||!rs[now]) now=ls[now]+rs[now]; else { int temp=rs[now]; while(ls[temp]) temp=ls[temp]; data[now]=data[temp]; del(rs[now],data[temp]); } } else if(data[now]<=v) del(rs[now],v); else del(ls[now],v); } int rank(int &now,int v) { if(!now) return 1; if(data[now]<v) return siz[ls[now]]+1+rank(rs[now],v); else return rank(ls[now],v); } int kth(int &now,int v) { if(!now) return -1; if(siz[ls[now]]+1==v) return data[now]; else if(siz[ls[now]]<v) return kth(rs[now],v-1-siz[ls[now]]); else return kth(ls[now],v); } int pred(int &now,int v) { if(!now) return -0x3f3f3f3f; if(data[now]<v) return max(data[now],pred(rs[now],v)); else return pred(ls[now],v); } int succ(int &now,int v) { if(!now) return 0x3f3f3f3f; if(data[now]<=v) return succ(rs[now],v); else return min(data[now],succ(ls[now],v)); } int main() { // freopen("data","r",stdin); // freopen("output","w",stdout); n=read(); while(n--) { scanf("%d%d",&op,&x); if(op==1) insert(root,x); else if(op==2) del(root,x); else if(op==3) printf("%d ",rank(root,x)); else if(op==4) printf("%d ",kth(root,x)); else if(op==5) printf("%d ",pred(root,x)); else printf("%d ",succ(root,x)); } return 0; } /* */