【算法】线段树与树状数组

LeeCode上线段树和树状数组的题目有如下,所有用树状数组可以解决的题都可以用线段树去解决,但是用线段树可以解决的,用树状数组则不一定能够去解:

  1. 218. The Skyline Problem
  2. 307. Range Sum Query - Mutable
  3. 308. Range Sum Query 2D- Mutable
  4. 315. Count of Smaller Numbers After Self而逆序对问题是用线段树和树状数组的经典问题,这个在之前的一篇算法总结(七)——逆序对问题的四种解法(归并排序,BST,树状数组,线段树)及变形里面已经总结过这两个在逆序对问题中的解法了。这里就不再赘述。

这里先列出树状数组和线段树的模板,以供以后使用:
一维树状数组:

vector<int> c(MAXN,0);
int lowbit(int x){
    return x&(-x);
}
int getsum(int x){
    int sum = 0;
    while(x>0){
        sum += c[x];
        x = x-lowbit(x);
    }
    return sum;
}
void update(int x,int val){
    while(x<MAXN){
        c[x] += val;
        x = x+lowbit(x);
    }
}

二维树状数组:

vector<vector<int>> c(MAXN,vector<int>(MAXM));

int lowbit(int x){
    return x&(-x);
}
int getsum_2d(int x,int y){
    int sum = 0;
    while(x>0){
        while(y>0){
            sum += c[x][y];
            y = y-lowbit(y);
        }
        x = x-lowbit(x);
    }
    return sum;
}
void update_2d(int x,int y,int val){
    while(x<MAXN){
        while(y<MAXM){
            c[x][y] += val;
            y = y+lowbit(y);
        }
        x = x+ lowbit(x);
    }
}

线段数模板
线段树需要注意几个问题:

  1. 线段树的大小要开到叶子结点的4倍才够
  2. 注意是否有叶子结点为0的情况,即传入的数组为空时的处理
  3. query和update的判断一定要注意,第二个判断一定是left>mid,不是left>=mid,因为mid的值是包在了左边的。
struct segtree{
    int left;
    int right;
    int count;
    segtree():left(0),right(0),count(0){
    }
}tree[MAXN];

void buildtree(int left, int right, int root){
    tree[root].left = left;
    tree[root].right = right;
    tree[root].count = 0;
    if (left == right)
        return;
    int mid = (left + right) >> 1;
    buildtree(left, mid, root << 1);
    buildtree(mid + 1, right, root << 1 | 1);
}
void update(int left, int right, int root, int value){
    tree[root].count += value;
    if (tree[root].left == tree[root].right)
        return;
    int mid = (tree[root].left + tree[root].right) >> 1;
    if (right <= mid)
        update(left, right, root << 1, value);
    else
    //注意这个地方一定是left>mid,不是left>=mid,因为mid的值是包在了左边的。
        if (left>mid)
            update(left, right, root << 1|1, value);
        else{
            update(left, mid, root << 1, value);
            update(mid + 1, right, root << 1 |1, value);
        }

}
int query(int left, int right, int root){
    if (left == tree[root].left&&right == tree[root].right)
        return tree[root].count;
    int mid = (tree[root].left + tree[root].right) >> 1;
    if (right <= mid)
        return query(left, right, root << 1);
    else
    //注意这个地方一定是left>mid,不是left>=mid,因为mid的值是包在了左边的。
        if (left > mid)
            return query(left, right, root << 1| 1);
        else{
            return query(left, mid, root << 1) + query(mid + 1, right, root << 1 |1);
        }

}

对于307题,基本上用模板就是秒杀了,不过需要注意的是如果用树状数组的话,求i到j之间的和,需要getsum(j+1)-getsum(i)不要忘了i这一项要减一

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值