【数据结构】线段树(入门)

简介: 【数据结构】线段树(入门)

一.原理:

1.结构:完全二叉树(不懂的点这个呀:传送门

2.可以维护的内容: sum,max,min等

struct node{
  int l,r;//左右端点
  int sum,maxx,minn;//要维护的值
}

3.示意图(直接盗用学长课件里的图啦)

线段树的每个节点表示一个区间,子节点则分别表示父节点的左右半区间,例如父亲的区间是[a,b],那么(c=(a+b)/2)左儿子的区间是[a,c],右儿子的区间是[c+1,b]。(来自课件)

(其实这个的原理跟模拟堆差不多,那篇博客被我鸽了 )

60a6bcefe26f4b118e50f46e4d0afd1d.png4.说明:

区间[l,r]一般分为[l,mid],[mid+1,r];
mid=floor(l+r>>1);//下取整

二.基本操作:

1.建树(递归)

2.单点修改:

3.区间查询:

(1)、如果这个区间被完全包括在目标区间里面,直接返回这个区间的值


(2)、如果这个区间的左儿子和目标区间有交集,那么递归左儿子


(3)、如果这个区间的右儿子和目标区间有交集,那么递归右儿子


三.代码:

1.pushup:用子节点更新当前节点信息

void pushup(int u){
    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}

2.build: 在一段区间上初始化线段树

void build(int u,int l,int r){
    if(l==r){
        tr[u].l=l;tr[u].r=r;tr[u].sum=w[r];
    }
    else{
        tr[u].l=l;tr[u].r=r;
        int mid=l+r >> 1;
        build(u << 1,l,mid);build(u<<1|1,mid+1,r);
        pushup(u);
    }
}

3.update:单点修改

void update(int u,int x,int v){
    if(tr[u].l==tr[u].r) tr[u].sum+=v;
    else{
        int mid=tr[u].l+tr[u].r >> 1;
        if(x<=mid) update(u<<1,x,v);
        else update(u<<1|1,x,v);
        pushup(u);
    }
}

4.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;
    if(l<=mid) sum=query(u<<1,l,r);
    if(r>mid) sum+=query(u<<1|1,l,r);
    return sum;
}

四.模板题

原题链接:传送门(更好的阅读体验)

(洛谷上也有几道模板题的 可以去做)


题目描述

给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [a,b] 的连续和。


输入格式

第一行包含两个整数 n 和 m,分别表示数的个数和操作次数。


第二行包含 n 个整数,表示完整数列。


接下来 m 行,每行包含三个整数 k,a,b (k=0,表示求子数列[a,b]的和;k=1,表示第 a 个数加 b)。


数列从 1 开始计数。


输出格式

输出若干行数字,表示 k=0 时,对应的子数列 [a,b] 的连续和。


数据范围

1≤n≤100000,

1≤m≤100000,

1≤a≤b≤n

输入样例:

10 5

1 2 3 4 5 6 7 8 9 10

1 1 5

0 1 3

0 4 8

1 7 5

0 4 8

输出样例:

11

30

35

线段树解法:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,m,w[maxn];
struct node{
    int l,r,sum;
}tr[4*maxn];
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=l;tr[u].r=r;tr[u].sum=w[r];
    }
    else{
        tr[u].l=l;tr[u].r=r;
        int mid=l+r >> 1;
        build(u << 1,l,mid);build(u<<1|1,mid+1,r);
        pushup(u);
    }
}
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;
    if(l<=mid) sum=query(u<<1,l,r);
    if(r>mid) sum+=query(u<<1|1,l,r);
    return sum;
}
void update(int u,int x,int v){
    if(tr[u].l==tr[u].r) tr[u].sum+=v;
    else{
        int mid=tr[u].l+tr[u].r >> 1;
        if(x<=mid) update(u<<1,x,v);
        else update(u<<1|1,x,v);
        pushup(u);
    }
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>w[i];
    build(1,1,n);
    int op,x,y;
    while(m--){
        cin>>op>>x>>y;
        if(!op) printf("%d\n",query(1,x,y));
        else update(1,x,y);
    }
    return 0;
}

树状数组解法:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+6;
int tr[N];
int a[N];
int n,m;
int lowbit (int x){
  return x&(-x);
} 
void add(int x,int y){
  for(int i=x;i<=n;i+=lowbit(i)) 
    tr[i]+=y;
}
int sum(int x){
  int res=0;
  for(int i=x;i>0;i-=lowbit(i)) res+=tr[i];
  return res;
}
int main(){
  cin>>n>>m;
  for(int i=1;i<=n;i++){
    cin>>a[i];
    add(i,a[i]);
  }
  while(m--){
    int k,x,y;
    cin>>k>>x>>y;
    if(k==0)
      cout<<sum(y)-sum(x-1)<<endl;
    else
      add(x,y);
  }
  return 0;
}

五:进阶-区间修改

60a6bcefe26f4b118e50f46e4d0afd1d.png

为了实现这个,引入一个新的状态——懒标记。

作用:存储到这个节点的修改信息,暂时不把修改信息传到子节点。

实现思路:

递归到这个节点时,只更新这个节点的状态,并把当前的更改值累积到标记中。

下传操作

①当前节点的懒标记累积到子节点的懒标记中。

②修改子节点状态。在引例中,就是原状态+子节点区间点的个数*父节点传下来的懒标记。

③父节点懒标记清0。

60a6bcefe26f4b118e50f46e4d0afd1d.png比较好的几篇博客呀:

线段树从零开始

线段树详解 (原理,实现与应用)

线段树 从入门到进阶


目录
相关文章
|
1月前
|
应用服务中间件 nginx C语言
Nginx入门 -- 基本数据结构中之ngx_str_t,ngx_array_t
这两种数据结构是Nginx自定义数据类型的例子,它们证明了Nginx设计者在构建一个为高并发和高性能优化的web服务器时的精确和高效。理解这些数据结构是深入学习Nginx内部机制的基础,同时也是扩展和开发Nginx模块不可或缺的一部分知识。
26 1
|
1月前
|
存储 算法 调度
10种 Python数据结构,从入门到精通
10种 Python数据结构,从入门到精通
25 0
|
2月前
|
算法 数据挖掘 计算机视觉
Python并查集实战宝典:从入门到精通,让你的数据结构技能无懈可击!
【7月更文挑战第17天】并查集,如同瑞士军刀,是解决元素分组问题的利器,应用于好友关系、像素聚类、碰撞检测和连通性分析等场景。本文从基础到实战,介绍并查集的初始化、查找与路径压缩、按秩合并,以及在Kruskal算法中的应用。通过并查集,实现高效动态集合操作,对比哈希表和平衡树,其在合并与查找上的性能尤为突出。学习并查集,提升算法解决复杂问题的能力。
63 5
|
3月前
|
存储 算法 Java
老程序员分享:java之数据结构【入门篇】
老程序员分享:java之数据结构【入门篇】
28 0
|
3月前
|
存储 安全 Java
Go语言入门之路——数据结构
Go语言入门之路——数据结构
116 0
|
3月前
|
机器学习/深度学习 算法
数据结构入门 时间 空间复杂度解析
数据结构入门 时间 空间复杂度解析
26 0
|
4月前
|
存储 NoSQL Java
Redis入门到通关之数据结构解析-Dict
Redis入门到通关之数据结构解析-Dict
64 2
|
4月前
|
存储 NoSQL 算法
Redis入门到通关之Redis数据结构-Hash篇
Redis入门到通关之Redis数据结构-Hash篇
59 1
|
4月前
|
存储 NoSQL Redis
Redis入门到通关之Redis数据结构-Set篇
Redis入门到通关之Redis数据结构-Set篇
65 1
|
4月前
|
存储 NoSQL Redis
Redis入门到通关之Redis数据结构-ZSet篇
Redis入门到通关之Redis数据结构-ZSet篇
219 1