写在前面:
- 要分清什么是定义域 ,什么是值域,
定义域: 可以理解为下标
值域: 可以理解为数组下标说对应的值 - 上一篇文章中,线段树是维护定义域的信息,也就是维护区间(下标)的信息,但是权值线段树,我们维护的值域的信息;
权值线段树:
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/2e748e42729f7f3fc6be67e4f02c34f6.png)
- 权值线段树值维护 L 到 R 的个数(在整个区间内)比如给定一个数组,树根表示整个区间内从1到4 的出现的个数,根的左根表示从 1到2的个数,右根表示从3 到4 出现的个数
- 下图详细说明呢权值线段树所能表达的信息
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/96b691c17a5333da1955f7c03d66fe58.png)
不管是权值线段树还是区间和线段树,首先要满足的是能够用父节点合并左子树和右子树
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+2;
int tree[maxn];
int data[maxn]={1,2,3,4,2,3,2,5,2,1,1,1,3,2,4};
const int nl=5;
int cnt[nl+1];
int nn=15;
void beg(){
for(int a=0;a<nn;a++){
cnt[data[a]]++;
}
}
void in(int node,int n,int l,int r){
if(l==r&&n==l){
tree[node]++;
return ;
}
int node_left=node* 2+1;
int node_right=node* 2+2;
int mid=l+r>>1;
if(mid>=n)in(node_left,n,l,mid);
else in(node_right,n,mid+1,r);
tree[node]=tree[node_left]+tree[node_right];
}
void build_tree(int node,int l,int r){
if(l==r){
tree[node]=cnt[l];
return ;
}
int node_left=node*2+1;
int node_right=node*2+2;
int mid=(l+r)/2;
build_tree(node_left,l,mid);
build_tree(node_right,mid+1,r);
tree[node]=tree[node_left]+tree[node_right];
}
void show(){
for(int a=0;a<15;a++){
cout<<tree[a]<<' ';
}
cout<<endl;
}
int pre(int node,int n,int l,int r){
cout<< l<<" "<<r<<endl;
if(l==r&&tree[node])return l;
int mid=l+r>>1;
int node_left=node*2+1;
int node_right=node*2+2;
if(mid>=n-1&&l<=n-1)return pre(node_left,n,l,mid);
else return pre(node_right,n,mid+1,r);
}
int hou(int node,int n,int l,int r ){
cout<< l<<" "<<r<<endl;
if(l==r&&tree[node])return l;
int mid=l+r>>1;
int node_left=node*2+1;
int node_right=node*2+2;
if(mid+1<=n+1&&r>=n+1)return hou(node_right,n,mid+1,r);
else return hou(node_left,n,l,mid);
}
int kth(int node,int n,int l,int r){
if(l==r)return l;
int node_left=node*2+1;
int node_right=node*2+2;
int mid=l+r>>1;
if(tree[node_left]>n)return kth(node_left,n,l,mid);
else return kth(node_right,n-tree[node_left],mid+1,r);
}
int rank(int node,int start,int end,int l,int r){
if(r<start)return 0;
if(l>end)return 0;
if(l==r)return tree[node];
if(l>=start&&r<=end)return tree[node];
int mid=l+r>>1;
int node_left=node*2+1;
int node_right=node*2+2;
int sum_left=rank(node_left,start,end,l,mid);
int sum_right=rank(node_right,start,end,mid+1,r);
return sum_left+sum_right;
}
main(){
beg();
build_tree(0,1,5);
cout<<pre(0,3,1,5)<<endl;
cout<<hou(0,3,1,5)<<endl;
}
- 权值线段树的插入 :
递归插入 如果数字比mid 小或等于,则进入左子树 否则进入右子树,最后定义递归的出口,就是当此时的值域的 R L 相等时。
void in(int node,int n,int l,int r){
if(l==r&&n==l){
tree[node]++;
return ;
}
int node_left=node* 2+1;
int node_right=node* 2+2;
int mid=l+r>>1;
if(mid>=n)in(node_left,n,l,mid);
else in(node_right,n,mid+1,r);
tree[node]=tree[node_left]+tree[node_right];
}
- 权值线段树 : 查找第 K 个数字 (先排序,再找第K )
如果左子树的值大于 k 那么进入左子树
否则 进入右子树查找 左子树-K 的值!
int kth(int node,int n,int l,int r){
if(l==r)return l;
int node_left=node*2+1;
int node_right=node*2+2;
int mid=l+r>>1;
if(tree[node_left]>n)return kth(node_left,n,l,mid);
else return kth(node_right,n-tree[node_left],mid+1,r);
}
- 查看 n 的排名
就是查看从 1 到 n-1 有多少个数字 ,跟求和线段树的思路一样
int rank(int node,int start,int end,int l,int r){
if(r<start)return 0;
if(l>end)return 0;
if(l==r)return tree[node];
if(l>=start&&r<=end)return tree[node];
int mid=l+r>>1;
int node_left=node*2+1;
int node_right=node*2+2;
int sum_left=rank(node_left,start,end,l,mid);
int sum_right=rank(node_right,start,end,mid+1,r);
return sum_left+sum_right;
}
- 还有查看前驱,后继的函数,就跟简单了,不用再说明呢,就跟普通的查找一样