线段树&树状数组代码模板

参考:线段树介绍

在这里插入图片描述

静态线段树

假设大小为n,则至少需要申请大小为4*n的数组,一般用在左右边界不大的情况,对应力扣:区间和的个数,代码模板:

class SegTree{
    int n;
    int[] arr;
    SegTree(int n){
        this.n = n;
        this.arr = new int[4*n];
    }
    public void update(int x){
        update(1,1,n,x);
    }
    public int query(int l, int r){
        return query(1,1,n,l,r);
    }
    private void update(int id, int l, int r, int val){
        if(val<l || r<val)  return;
        arr[id]++;
        if(l==r)    return;
        int mid = l+(r-l)/2;
        update(id<<1, l, mid, val);
        update((id<<1)+1, mid+1, r, val);
    }
    private int query(int id, int l, int r, int tl, int tr){
        if(tr<l || r<tl)    return 0;
        else if(tl<=l && r<=tr) return arr[id];
        int mid = l+(r-l)/2;
        return query(id<<1, l, mid, tl, tr) + query((id<<1)+1, mid+1, r, tl, tr);
    }
}
动态线段树

就是建立一颗二叉树,每个节点包括左区间(left)和右区间(right),一般用在左右边界很大并且数据之间间隔也很大的情况(比如-inf,0,1000,inf),对这些数据进行排序并用哈希map离散化后,可以将左右边界缩小,便于存入树中,该模板内部封装一个建树的静态方法,对应力扣通过指令创建有序数组,考虑到该题目数值可能会很大,所以采用动态构建,当然静态也可以除非你的内存很刚,对应模板:

java模板:

class SegNode{
    int left,right;
    int sum = 0;
    SegNode left_child, right_child;
    SegNode(int left, int right){
        this.left = left;
        this.right = right;
    }
    public void update(int val){
        if(val<left || right<val)  return;
        sum++;
        if(left==right)    return;
        left_child.update(val);
        right_child.update(val);
    }
    public int query(int l, int r){
        if(r<left || right<l)    return 0;
        else if(l<=left && right<=r) return sum;
        return left_child.query(l,r)+right_child.query(l,r);
    }
    public static void print(SegNode node){
        if(node==null)  return;
        System.out.println(node.left+" "+node.right+" "+node.sum);
        print(node.left_child);
        print(node.right_child);
    }
    public static SegNode build(int min, int max){
        SegNode root = new SegNode(min,max);
        if(min==max)    return root;
        int mid = min+(max-min)/2;
        root.left_child = build(min, mid);
        root.right_child = build(mid+1, max);
        return root;
    }
}

C++模板(更新时间20220113),对上面的java模板进行优化,加入懒开辟,即不在在使用SetTree之前就把MIN~MAX范围的所有元素准备好,而是动态运行时开辟,直接上模板:

class SegNode {
public:
    int left_edge, right_edge;
    int sum = 0;
    SegNode *left_child, *right_child;

    SegNode(int left_edge, int right_edge) : left_edge(left_edge), right_edge(right_edge), left_child(nullptr),
                                             right_child(nullptr) {}

    void update(int val, int num) {
        if (val < left_edge || right_edge < val) return;
        sum += num;
        if (left_edge == right_edge) return;
        int mid = left_edge + (right_edge - left_edge) / 2;
        // 动态运行时开辟
        if (left_child == nullptr) left_child = new SegNode(left_edge, mid);
        if (right_child == nullptr) right_child = new SegNode(mid + 1, right_edge);
        left_child->update(val, num);
        right_child->update(val, num);
    }

    int query(int l, int r) {
        if (r < left_edge || right_edge < l) return 0;
        if (l <= left_edge && right_edge <= r) return sum;
        // 如果左右孩子为空,则说明之前木有遍历过这里,直接返回0
        return (left_child != nullptr ? left_child->query(l, r) : 0) +
               (right_child != nullptr ? right_child->query(l, r) : 0);
    }
};

区间查找,区间查询(线段树),采用懒查询的方式,提高时间复杂度
例题:LCP 05

class SegTree:# 区间增加,区间查询
    def __init__(self, N: int):
        self.N = N
        self.value = [0] * 4 * N
        self.lazy = [0] * 4 * N
        self.MOD = 1e9+7

    def __push_down(self, id:int, left:int, right:int) -> None: #用于懒查询,因为不一定将来会查询到这里,这么做可以减少时间复杂度
        if self.lazy[id] == 0:  return
        self.value[id] += self.lazy[id]*(right-left+1)
        if left < right:
            self.lazy[id << 1] += self.lazy[id]
            self.lazy[(id << 1) | 1] += self.lazy[id]
        self.lazy[id] = 0

    def update(self, left: int, right: int, value: int) -> None:
        self.__update(1, 1, self.N, left, right, value)

    def __update(self, id:int, opleft:int, opright:int, left:int, right:int, value:int) -> None:
        self.__push_down(id, opleft, opright)
        if right < opleft or opright < left:    return
        if  left <= opleft and opright <= right:
            self.lazy[id] = value
            self.__push_down(id, opleft, opright)
            return
        mid = (opleft+opright)>>1
        self.__update(id<<1, opleft, mid, left, right, value)
        self.__update((id<<1)|1, mid+1, opright, left, right, value)
        self.value[id] = int((self.value[id<<1] + self.value[(id<<1)|1]) % self.MOD)

    def quire(self, left: int, right: int) -> int:
        return self.__quire(1, 1, self.N, left, right)

    def __quire(self, id: int, opleft: int, opright: int, left: int, right: int) -> int:
        if right < opleft or opright < left:    return 0
        self.__push_down(id, opleft, opright)
        if  left <= opleft and opright <= right:    return self.value[id]
        mid = (opleft + opright) >> 1
        return int((self.__quire(id << 1, opleft, mid, left, right) + \
               self.__quire((id << 1) | 1, mid + 1, opright, left, right)) % self.MOD)

树状数组

模板(C++版):

class BinaryIndexTree{
public:
    vector<T> arr;
    BinaryIndexTree(int n){
        arr = vector<T>(n, 0);
    }
    int lowbit(int n){
        return n&(-n);
    }
    void update(int x, T num){
        for(int i=x; i<SIZE; i+=lowbit(i)){
            arr[i] += num;
        }
    }
    T get(int x){
        T ans = 0;
        for(int i=x; i>0; i-=lowbit(i)){
            ans += arr[i];
        }
        return ans;
    }
};

例题:数字流的秩

#define N 500001
class StreamRank {
public:
	// 可以计算从0~50000之间
    int arr[N+1];
    StreamRank() {
        memset(arr, 0, sizeof(arr));
    }

    int lowbit(int x){
        return x&(-x);
    }

    void add(int x, int num){
        for(int i=x; i<=N; i+=lowbit(i))    arr[i] += num;
    }

    int query(int x){
        int ans = 0;
        for(int i=x; i>0; i-=lowbit(i))     ans += arr[i];
        return ans;
    }
	// 考虑到不能够为0,所以从x+1开始算
    void track(int x) {
        add(x+1, 1);
    }

    int getRankOfNumber(int x) {
        return query(x+1);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值