ZKW线段树

一般的线段树采用的是自顶向下递归建树的办法,但这样写起来比较麻烦,于是有人就想着能不能有自底向上的建树,ZKW线段树就出现了。

基于二叉树的性质我们知道,对于一个叶子节点数为n(2的k次方)的满二叉树,它的第一个叶子节点标号同样为n,于是对于一个有m个点的区间,我们可以假设它是一个满二叉树,只不过一部分数据为0,于是我们只需要找到大于m的最小n,并顺序输入即可建树

void down(int i)//自底向上建树
{
    tree[i]=tree[i>>1]+tree[i>>1|1];
}

void build(int m)
{
    int n,i;
    for(n=1;n<m;n<<=1);                        //寻找第一个叶子的位置    
    for(i=n+1;i<=n+m;i++) scanf("%d",&tree[i]);//这里从n+1开始输入的原因后面会提到
    for(i=n-1;i>=1;i--) down(i);
}

建好树了以后,我们尝试对它进行单点跟新,同样,思路为从叶子节点开始向上跟新

void Insert(int i,int k)
{
    int nood=n+i;                           //n为建树时所求本来第一个叶子节点的位置
    tree[nood]=k;
    for(nood>>=1;nood>=1;nood>>=1) down(nood);
}

最后是求和,自底向上的求和跟普通求和不太一样。思路是对于区间[l,r],判断若l为l/2的左子树,那么l+1一定在所求范围之内,对于r同理,然后不断循环向上直到l与r为兄弟节点。为了达到这个目的,我们需要将题目所求区间[l,r]转化为[l-1,r+1]进行求解,这也是为什么之前建树从n+1开始的原因,若从n开始建树当所求区间l=1时l-1会跳到上一级(求和过程可能没那么好理解自己画个图手动模拟一下会有些帮助)。

void sum(int l,int r)
{
    int s=0;
    l=l+m-1;
    r=r+m+1;
    //分别将l与r转化为l-1与r+1
    for(;l^r^1;l<<=1,r<<=1) //l^r^1为判断l与r是否是兄弟节点
    {
        if(!l&1)          //l为l/2左节点
            s+=tree[l^1]; //加上l/2右节点的值
        if(r&1)           //r为r/2右节点
            s+=tree[r^1]; //加上r/2左节点的值
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值