划分树
划分树的定义
它的每一个节点保存区间[lft, rht]所有元素,元素排列顺序与原数组(输入)相同,但是,两个子树的元素为该节点所有元素排序后 rht−lft +1 / 2个进入左子树,其余的到右子树,同时维护一个num域,num[i] 表示lft 到i这些点有多少进入了左子树。
数据结构
int Sort[M];//排完序的数组,便于后面查找一段线段的最中间的数
struct node{
int val[M];//记录第几层的位置保存那个值
int num[M];//记录从lft到i这些点多少个进入左子树
}t[20];//不同的层,见下面的例子
输入 :1 2 3 44 5 6 8
排完序:1 2 3 4 4 5 6 8
建树:[1,5,6,3,8,4,4,2]
[1,3,4,2][5,6,8,4]
[1,2][3,4][5,4][6,8]
[1][2][3][4][4][5][6][8]
标记红色的表示进入下一层的左节点
划分树的建立Build
划分树的建立和普通的二叉树的建立过程差不多,一开始排序原数组的一个copy,每次在树的一层根据排好序的数组对这个节点长度的数进行操作,具体就是把一半的数存入下一层左节点,一半的数存在右节点,当然是左节点数小于右节点的数最小值,不断维护num的值,num值的作用是方便查找
void build_tree(intlft,int rht,intp){在第p层区间[lft,rht]建树
if(lft==rht)return ;//只有一个节点直接返回
int i,mid =md(lft,rht);//区间中点
int isame =mid-lft+1,same = 0;//假设一开始区间的左半部分所有的数都和这个区间最中间,i的数相等isame,same标记多少个相等的放在左节点了
for(i =lft;i<=rht;i++)
if(t[p].val[i]<Sort[mid])isame--;///这样可以求出左边有多少个数要放和Sort[mid]相等
int ln =lft,rn = mid+1;
for(i =lft;i<=rht;++i){
if(i==lft)t[p].num[i]=0;//特别处理特殊情况
elset[p].num[i]=t[p].num[i-1];
///处理三种可能小于 大于 等于,等于还要注意是否已经放够了相等的数
if(t[p].val[i]<Sort[mid]){
t[p].num[i]++;
t[p+1].val[ln++]=t[p].val[i];
}else if(t[p].val[i]>Sort[mid]){
t[p+1].val[rn++]=t[p].val[i];
}else {
if(same<isame){
same++;
t[p].num[i]++;
t[p+1].val[ln++]=t[p].val[i];
}else{
t[p+1].val[rn++]=t[p].val[i];
}
}
}
///递归对子节点建树
build_tree(lft,mid,p+1);
build_tree(mid+1,rht,p+1);
}
划分树的查找
在区间[a,b]上查找第k大的元素当前树节点的区间[lft,rht]
1. 如果t[p].num[b]−t[p].num[a−1]>=k, 即区间[a,b]进入p的左孩子的个数已经超过k个,那么就往左孩子里面查找,同时更新a,b
a = lft+t[p].num[a-1],b=lft+t[p].num[b]-1;
这个要自己仔细想一下了,结合上面树的结构和num的作用,其实就是不断缩短查找区间
2. 否则,即[a,b]进入p的左孩子的个数小于k个,那么就要往右孩子查找第 (k-s)大的数,同理,a = mid+1+(a-lft-t[p].num[a-1])括号部分表示[lft,a-1]多少个进入有p的右子树,清楚了吧。b同理,自己想想。。
int query(int a,int b,int k,int p,int lft,int rht){
if(lft==rht)return t[p].val[lft];
int s,ss,b2,bb,mid=md(lft,rht);
if(a==lft){
s = t[p].num[b];
ss = 0;
}else {
s = t[p].num[b]-t[p].num[a-1];
ss = t[p].num[a-1];
}
if(s>=k){
a = lft+ss;
b = lft+ss+s-1;
returnquery(a,b,k,p+1,lft,mid);
}else {
bb = a-lft-ss;
b2 = b-a+1-s;
a = mid+bb+1;
b = mid+bb+b2;
returnquery(a,b,k-s,p+1,mid+1,rht);
}
}
完整的code 取自poj 2104
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define M 100002
#define md(x,y) (((x)+(y))>>1)
int Sort[M];
struct node{
int val[M];
int num[M];
}t[20];
void build_tree(intlft,int rht,intp){//区?间?[lft,rht],第̨²p层?
if(lft==rht)return ;
int i,mid =md(lft,rht);
int isame =mid-lft+1,same = 0;
for(i =lft;i<=rht;i++)
if(t[p].val[i]<Sort[mid])isame--;
int ln =lft,rn = mid+1;
for(i =lft;i<=rht;++i){
if(i==lft)t[p].num[i]=0;
elset[p].num[i]=t[p].num[i-1];
if(t[p].val[i]<Sort[mid]){
t[p].num[i]++;
t[p+1].val[ln++]=t[p].val[i];
}else if(t[p].val[i]>Sort[mid]){
t[p+1].val[rn++]=t[p].val[i];
}else {
if(same<isame){
same++;
t[p].num[i]++;
t[p+1].val[ln++]=t[p].val[i];
}else{
t[p+1].val[rn++]=t[p].val[i];
}
}
}
build_tree(lft,mid,p+1);
build_tree(mid+1,rht,p+1);
}
int query(int a,int b,int k,int p,int lft,int rht){
if(lft==rht)return t[p].val[lft];
int s,ss,b2,bb,mid=md(lft,rht);
if(a==lft){
s = t[p].num[b];
ss = 0;
}else {
s = t[p].num[b]-t[p].num[a-1];
ss = t[p].num[a-1];
}
if(s>=k){
a = lft+ss;
b = lft+ss+s-1;
returnquery(a,b,k,p+1,lft,mid);
}else {
bb = a-lft-ss;
b2 = b-a+1-s;
a = mid+bb+1;
b = mid+bb+b2;
returnquery(a,b,k-s,p+1,mid+1,rht);
}
}
int main()
{
int n,m,k;
scanf("%d%d",&n,&m);
for(int i = 0;i<n;++i){
scanf("%d",&t[0].val[1+i]);
Sort[i+1] = t[0].val[i+1];
}
sort(Sort+1,Sort+1+n);
build_tree(1,n,0);
/*for(int i =0;i<=3;i++){
printf("\nnum[i]:\t");
for(int j = 1;j<=n;j++)
printf("%d ",t[i].num[j]);
} */
for(int i = 0;i<m;i++){
intst,ed;
scanf("%d%d%d",&st,&ed,&k);
printf("%d\n",query(st,ed,k,0,1,n));
}
return 0;
}