现在我们需要写一种数据结构来维护一些数(都是 1 0 9 10^9 109 以内的数字)的集合,最开始时集合是空的。其中需要提供以下操作,操作次数 q q q 不超过 10000 10000 10000。
- 查询x数的排名(排名定义为比当前数小的数的个数 + 1 +1 +1。若有多个相同的数,应输出最小的排名)。
- 查询排名为x的数。
- 查询x的前驱和后继
- 插入一个数 x。
对于这个需求,我们可以使用暴力枚举的方法,也可以使用二叉搜索树完成。
二叉搜索树的性质
二叉搜索树具有以下的性质:
- 若结点x的左子树不空,则x左子树中所有结点的值均小于结点x的值。
- 若结点x的右子树不空,则x右子树中所有结点的值均大于结点x的值。
- 任意结点的左、右子树也分别是二叉搜索树。
- 没有键值相等的结点。
下面是一棵二叉搜索树
节点设计
可以对每个结点定义5个变量:
- 用left 表示左儿子
- right表示右儿子
- value 表示该结点的权值
- size 表示以该结点为根结点的子树的结点个数
- num 表示该结点权值出现的次数。
功能设计
- 查询
x
x
x 数的排名:
每次将 x x x 和根结点 r o o t root root 的权值比较,如果 x x x 小于根结点 r o o t root root 的权值,那么 r o o t root root 的右子树里面的所有权值都比 x x x 要大,所以递归下去查询左子树。如果 x x x 大于根结点 r o o t root root 的权值,那么 r o o t root root 的左子树里面的所有权值都比 x x x 要小,所以递归下去查询右子树,并且把左子树的大小加人到答案里面。如果 x x x 等于根结点 r o o t root root 的权值,那么我们已经找到了 x x x ,返回答案即可。 - 查询排名为
x
x
x 的数:
当査询 r o o t root root 子树中第 x x x 小的值时:- 如果 x x x 小于或等于 r o o t root root 左子树的大小,则排名为 x x x 的数一定在 r o o t root root 的左子树中,递归下去査询 r o o t root root 的左子树中排名为 x x x 的数;
- 如果x等于 root 左子树的大小 +1,由于左子树里面的权值都小于根结点 r o o t root root 的权值,右子树里面的权值都大于根结点 r o o t root root 的权值,所以排名为 x x x 的数一定是根结点 r o o t root root 的权值,将其返回即可;
- 如果 x x x 大于 “ r o o t root root 左子树的大小+1”,则排名为 x x x 的数一定在 r o o t root root 的右子树中,递归下去查询 r o o t root root 的右子树中排名为“ x − 左子树大小 − 1 的数 x-左子树大小 -1的数 x−左子树大小−1的数”;
- 求x的前驱后继:
不用专门写两个函数来查询前驱后继,可以先查询 x x x 的排名 r a n k rank rank ,然后查询排名为 r a n k − 1 rank-1 rank−1 的数与排名为 r a n k + 1 rank+1 rank+1 的数,这两个查询结果即分别为 x x x 的前驱和后继。 - 插入一个数
x
x
x:
每次将 x x x 和根结点 r o o t root root 的权值比较。如果 x x x小于根结点 r o o t root root 的权值,那么把 x x x插人 r o o t root root 的左儿子里面;如果 x x x 大于根结点 r o o t root root 的权值,那么把 x x x 插入 r o o t root root 的右儿子里面,这样操作之后这个二叉搜索树仍然保持了其性质。如果此时将 x x x 插入的那个位置的结点并不存在,比如要将 x x x 插人 r o o t root root 的左子树中,但是 r o o t root root 的左儿子是空的,则新建一个结点,权值为 x x x,来代替那个不存在的结点,然后回溯的时候更新该结点的 s i z e size size 值。
code:
#include<bits/stdc++.h>
using namespace std;
int n;
struct node{
int val, num, size;
int left, right;
node(int v, int s, int nn, int l, int r):val(v), num(nn), size(s), left(l), right(r){};
node(){}
};
node t[2000];
void flatten(int root){
//更新节点信息
t[root].size = t[t[root].left].size + t[t[root].right].size + t[root].num;
}
void build(int root, int loc){
//插入节点 / 建树
if(t[loc].val < t[root].val){
if(t[root].left == -1){t[root].left = loc;}
else build(t[root].left, loc);
}
else if(t[loc].val > t[root].val){
if(t[root].right == -1)t[root].right = loc;
else build(t[root].right, loc);
}
else t[root].num++;
flatten(root);
}
int rank(int x, int root){
//查找x的排名
if(root){
if(x < t[root].val){
return rank(x, t[root].left);
}
if(x > t[root].val){
return rank(x, t[root].right) + t[t[root].left].size + t[root].num;
}
return t[t[root].left].size+t[root].num;
}
return 1;
}
int find_kth(int root, int k){
//查找排名x的值
if(k <= t[t[root].left].size){
return find_kth(t[root].left, k);
}
else if(k <= t[root].num + t[t[root].left].size){
return t[root].val;
}
else return find_kth(t[root].right, k - t[root].num - t[t[root].left].size);
}
bool find_value(int root, int v){
//查找是否存在值x
if(v == t[root].val)return true;
else if(v < t[root].val && t[root].left != -1) return find_value(t[root].left, v);
else if(v > t[root].val && t[root].right != -1) return find_value(t[root].right, v);
return false;
}
int main(){
int root = 1; cin >> n;
for(int i = 1; i <= n; i++){
t[i] = node(0, 1, 1, -1, -1);
cin >> t[i].val;
if(i != 1)build(root, i);
}
}