线段树图解及模板

线段树一般用来做单点修改区间查询操作
树状数组是多叉树,线段树是一个二叉树

struct Node{
	int l,r;//l,r属于1~n ,是原数组的下标(从1开始)
	int sum;
} tr[N * 4];
int w[N];

image.png

单点修改 O(logn)

本质是一个递归和回溯的过程
image.png

区间查询 O(logn)

image.png

与前缀和对比


单点修改区间查询
前缀和 O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)
线段树 O ( l o g n ) O(logn) O(logn) O ( l o g n ) O(logn) O(logn)

主要方法

pushup用子节点信息更新当前节点信息
build在一段区间上初始化线段树
modify修改
query查询

pushup

u << 1 = 2 * u u << 1 | 1 = 2 * u + 1

void pushup(int u) {//其实就是回溯的过程,每个结点的sum值等于左右儿子的sum值之和
	tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}

build

void build(int u, int l, int r) {
	if(l == r) tr[u] = {l, r, w[r]}; // 叶子节点直接赋值
    else {
        tr[u] = {l,r};//下面的pushup操作只能更新sum值,所以还需要将这一个结点的l和r赋值 
        
        int mid = l + r >> 1;
        build(u << 1, l, mid); // 更新左儿子
        build(u << 1 | 1, mid + 1, r); // 更新右儿子
        pushup(u); // 把信息往上传递更新父节点
    }
}

modify

void modify(int u, int x, int v) {
	if(tr[u].l == tr[u].r) tr[u].sum += v; // 叶子节点直接加上这个v
    else {
        int mid = tr[u].l + tr[u].r >> 1;
        //注意:递归时x,v都不变
        
        if(x <= mid) modify(u << 1, x, v);
        else modify(u << 1 | 1, x, v);
        pushup(u);//往上回溯,更新所有经过节点的sum值 
    }
}

query

int query(int u, int l,int r) {
    //如果序列完全包含这个子区间 则直接返回
	if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
    int mid = tr[u].l + tr[u].r >> 1;
    int sum = 0;
    //注意query递归时的参数l,r都不发生变化 ,变化的参数只是左右儿子的坐标u
    if(l <= mid) sum = query(u << 1, l, r);
    if(r > mid) sum += query(u << 1 | 1, l, r);
    return sum;
}

线段树数组为什么是4N?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值