线段树 以及树状数组

树状数组 O(logn)
n/2+n/4+…+1 =2n 这是从1-n-1层的节点数
假设最后一层节点数 是2n 那么一共的节点数 是4n

主要解决的问题: 单点修改 区间查询
前缀和O(1)只能解决区间查询问题 并不能为我们解决单点修改问题
     前缀和 用树状数组
单点修改   O(n)   O(logn)
区间查询   O(1)  O(logn)
总时间复杂度 O(n) O(logn)
树状数组两个操作:
void add(int x, int v)   在x位置上+v
int query(int x)   查询从1-x区间上的和
注意:

一般能用树状数组解决的问题  我们就用树状数组解决;
如果树状数组不能解决的问题  我们用线段树解决

注意:线段树 我们都是从根节点1开始操作的

线段树我们存储结点是 用结构体数组来存储的(与heap相似)
主要解决的问题: 单点修改 区间查询
线段树四个操作:
void pushup(int u)
{
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum; //用子节点信息更新当前节点信
}
//在一段区间初始化线段树
void build(int u, int l, int r)
{
if (l == r) tr[u] = {l, r, w[r]}; //如果我们当前是叶子节点 那么这个权值就是w[r/l]
else
{
tr[u] = {l, r};
int mid = l + r >> 1; //否则我们就要二分
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u); //最后回溯时候 我们更新当前这个结点的权值
}
}
//从根节点开始 查询a~b区间的和
int query(int u, int l, int r)
{
//如果我们当前区间的数据在a~b区间 也就是被包含 我们直接返回sum即可
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;
if (l <= mid) sum = query(u << 1, l, r);
if (r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
//表示第 a 个数加 b
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;
    if (x <= mid) modify(u << 1, x, v);
    else modify(u << 1 | 1, x, v);
    //因为我们修改的是值  所以我们也需要给这个叶子节点的父亲节点 +v
    pushup(u);
}

}

你可能感兴趣的:(线段树 以及树状数组)