线段树及空间开4倍



下面我们来讲解线段树:


    线段树有许多应用,给出一个序列,可以在任何一个区间内找到最大,和最小值。可以求区间和等等等等。那么应用就不多说了。毕竟能到这里来的我相信都是为了A题,并且了解线段树的吧!

    废话不多说了。

      线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
      对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
      使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。当然有兴趣的朋友可以去百度一下离散化去压缩空间。

    下面我们来建树:

void pushUp(int rt){
    Array[rt]=Array[rt<<1]+Array[rt<<1|1];//当最后1排度为2的父节点的两个子节点存储了值后当然就要更新父节点了。
    return;
}
void buildTree(int l,int r,int rt){//rt代表根节点,l代表左边界,r代表右边界
    if(l==r){
        scanf("%d",&Array[rt]);//Array代表存储节点的数组
        return;
    }
    int mid=(l+r)>>1;
    if(l<=mid){
        buildTree(l,mid,rt<<1);
    }
    if(r>mid){
        buildTree(mid+1,r,rt<<1|1);//这个rt<<1代表乘2, |1代表加1,位运算快多了。 
    }
    pushUp(rt);
    return ;
}
    
    下面我们就来贴出完整的代码:
    
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,t;
int Array[100000<<2];//100000乘4

void pushUp(int rt){
    Array[rt]=Array[rt<<1]+Array[rt<<1|1];
    return;
}
void buildTree(int l,int r,int rt){
    if(l==r){
        scanf("%d",&Array[rt]);
        return;
    }
    int mid=(l+r)>>1;
    if(l<=mid){
        buildTree(l,mid,rt<<1);
    }
    if(r>mid){
        buildTree(mid+1,r,rt<<1|1);
    }
    pushUp(rt);
    return ;
}
int query(int left,int right,int l,int r,int rt){
    if(left<=l&&r<=right){//查找区间内的值是就返回
        return Array[rt];
    }
    int mid=(l+r)>>1;
    int res=0;
    if(mid>=left){
        res+=query(left,right,l,mid,rt<<1);
    }
    if(mid<left){
        res+=query(left,right,mid+1,r,rt<<1|1);
    }
    return res;
}
int main(){
    while(~scanf("%d%d",&n,&t)){
        buildTree(1,n,1);
        char order[10];
        while(t--){
            scanf("%s",order);
            int left,right;
            if(order[0]=='Q'){
                scanf("%d%d",&left,&right);
                printf("%d\n",query(left,right,1,n,1));
            }
        }
    }
    return 0;
}
  下面很多人就要问了,为什么开数组为什么要开N*4的空间,好,我们来看看这张图。

这是metlab画的图


    由图可以很生动形象的看出,为什么很多人开2*N会越界了。图中的X是节点的总数。
    但是我不想画图又怎么办呢?推公式??OK下面是我的一个朋友推出来的公式!!!
    假设N为节点的总数,No代表度为0的节点的总数。N2代表度为2的节点的总数!
由此可得:

    倍数= N(max)/No    -->No也相当于区间的长度。

    N(max)可以看做是一个满二叉树(最好的情况)。N(min)可以看做最后一层只有两个子节点的树(最坏的情况)。
    

两个节点的树


    _
    |
     N = No + N2
     N = 2 * N2 + 1
    |_

    k代表层数。
    把N2消掉后可以得到。No = (N + 1) / 2
    N(max)=2^k  -  1
    N(k-1层以上的节点总数)=2^(k-1) + 1
    所以:No = [(2^(k-1)+1)+1]/2
    倍数= N(max)/No
    化简可以得到 4- 5/(2^(k-2) + 1 )
    -->4-5/(2^(k-1)+2)/2
    -->4-5/No
    即要开的空间倍数就是4-5/No
    
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值