C++基础:线段树(基础)

目录

声明

正文

区间问题和线段树

线段树的建立

线段树的单点更新

 线段树的查询

单点查询

区间查询

 


声明

线段树理解起来不难,但是题灵活多变,难,建议学之后多刷题

正文

区间问题和线段树

有一类区间问题可以抽象成如下模型。

给定包含 n 个数的数组 a1​,a2​,⋯an​。有两种操作

  1. 查询区间 [l,r] 最小的数。

  2. 修改第 ai​ 为 x。

这里,为了解决这个问题,我们介绍一种灵活的数据结构——线段树

我们用一棵二叉树来表示线段树,线段树中的每个结点都表示一个区间。每个非叶子结点都有左右两棵子树,分别对应区间的 "左半" 和 "右半"。为了方便起见,我们给根结点编号为 1。对于每个结点,其左结点的编号为 2i,其右结点的编号为 2i+1。

对于一个结点,如果其表示的区间为 [l,r]。分情况如果 l=r,那么这个是一个叶子结点。否则令mid=⌊(l+r)÷2⌋ (注意:非中括号,是下取整),左儿子对应的区间为 [l,mid],右儿子对应的区间为 [mid+1,r],这一思想有点类似二分。下面就是 n=10 的时候的线段树。

 假定根结点表示长度为 2^h 的区间,不难发现,树的第 i 层有 2^i 个结点,每个结点对应一个长度为 2^(h-i) 的区间。最大层的编号为 h,结点总数为 1+2+4+8+⋯+2^h=2^(h+1)−1,略小于区间长度的两倍。而当整个区间长度不是 2 的整数幂时,虽然叶子结点不在同一层,但树的最大层编号和结点总数仍满足上述结论。

线段树的建立

前面构建的线段树,只是展示了线段树中各结点所对应的区间,但是对于用到线段树的大部分题目来说,这些线段所拥有的附加信息才是重头戏。比如要维护区间最小值问题,我们用一个额外的数组minv记录每个结点对应的区间的最小值。

对于叶子结点,最小值就是一个数。而对于非叶子结点,区间的最小值就是左儿子的最小值和右儿子最小值中的最小值。

比如 n=10,a=1,3,5,7,9,10,2,4,8,6 的时候,对应的线段树如下

 可以发现这个构建过程是一个递归的过程,父节点的信息需要用子节点去更新,所以我们需要先递归的构建好左右子树。见下面代码。

const int maxn = 10010;
int minv[4 * maxn], a[maxn];
// id 表示结点编号,l, r 表示左右区间
void build(int id, int l, int r) {
    if
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值