权值线段树-线段树的一种-能够确定定义域的值域的信息

写在前面:

  • 要分清什么是定义域 ,什么是值域,
    定义域: 可以理解为下标
    值域: 可以理解为数组下标说对应的值
  • 上一篇文章中,线段树是维护定义域的信息,也就是维护区间(下标)的信息,但是权值线段树,我们维护的值域的信息;

权值线段树:

在这里插入图片描述

  • 权值线段树值维护 L 到 R 的个数(在整个区间内)比如给定一个数组,树根表示整个区间内从1到4 的出现的个数,根的左根表示从 1到2的个数,右根表示从3 到4 出现的个数
  • 下图详细说明呢权值线段树所能表达的信息
    在这里插入图片描述
    不管是权值线段树还是区间和线段树,首先要满足的是能够用父节点合并左子树和右子树
#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;
}
  • 还有查看前驱,后继的函数,就跟简单了,不用再说明呢,就跟普通的查找一样

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值