初识线段树(线段树的来源与基本结构)

本文介绍了线段树这一数据结构,用于解决数组中频繁的区间求和与单点更新操作。通过对比简单的数组和前缀和方案,分析了在操作次数相当且较多的情况下,线段树如何利用分治思想,将时间复杂度从O(n)降低到O(logn),从而提高效率。线段树不仅适用于求和,还可应用于各种涉及整体与个体关系的问题中。
摘要由CSDN通过智能技术生成

鲁迅说过:一项科技或技术被发明出来,一定是遇到了之前无法解决的问题。线段树也不例外

问题提出:

我们有一个数组,我们需要频繁地对数组执行以下两个操作:

  • 求数组中下标为[ l - r ] 的和
  • 对数组中下标为 i 的元素的值进行更新

初步方案:

方案一:

最简单的方式,我们在计算机中使用数组存储这一串数字。

  • 针对问题一,我们直接遍历求和,复杂度为O(n)
  • 针对问题二,我们使用数组可以直接进行更新,复杂度为O(1)

方案二:

当然了,我们也可以选择使用前缀和来对问题一进行优化。具体的做法是:

  • 维护一个前缀和数组,pre[ i ] 表示前 i 个元素的和,可以提前使用O(n)的复杂度将数组维护好。
  • 针对问题一,我们就可以使用O(1)的复杂度求得
  • 针对问题二,我们就需要使用O(n)的复杂度对前缀和进行维护。

这两种方式可以根据问题的规模的不同来适当的取舍,在实际问题中,如果更新的次数远大于求和的次数,并且需要求和的次数又比较少,那么使用方案一不失为一种好的方案。同样的,如果求和的次数远大于更新的次数,而更新的次数又比较少,那么我们使用方案二也是非常优秀的。

思考:

如果两个操作出现的次数相当,并且都非常多的话,显然,整体O(n)的复杂度是不能忍受的。我们需要更好的方案。

在优化的时候可以考虑分治的思想

思路从前缀和而来,我们在维护这个前缀和的时候,本质上只维护了一个和,那就是所有元素到起始元素的和。所以其中某一个元素修改之后,后面所有的前缀和就都要修改。这实际上对资源是一种浪费。

如果我们在更新一个元素后,只修改部分的 “前缀和”,是不是就可以将复杂度降下去呢?

比如说,我把全部的元素分为两个数组,维护两个前缀和,那么在计算区间和的时候,如果全部落在一个区间中,直接在那个区间中计算即可,如果落在两个区间的交界处,对两个分别进行求和再相加即可,时间复杂度同样为O(1)。如下图所示:
在这里插入图片描述
在修改元素的时候,我们维护前缀,时间复杂度会从O(n)到O(n)。想必这里读者应该看出不一样的地方了吧,我在分成两半的时候,时间复杂度就会变成原来的二分之一,因为我只需要维护最多一个区间上的前缀和,虽然还是O(n)级别,但是说明我们已经对其进行了优化。

那么如果我们分的更多一点呢?事实证明,我们分的越多,我们在维护所谓“前缀和”的时候,需要花费的时间就越少。同时,在求区间和的时候,需要进行的判断也就越多,这里的时间花销也同样会增长,那么如何让其进行一个平衡呢?

引入线段树:

这里需要一点点二叉树的知识

我们直接说明他是如何实现的,如下图所示:
在这里插入图片描述
假如数组中有六个元素,那么我们就需要构造这样一棵树,这一颗树的根节点的值表示元素1~6的和,其左子树和右子树将其父节点表示的范围分成两半,左子树表示元素1 ~ 3的和,右子树表示元素4 ~ 6的和,以此类推,当遇到节点表示的和的个数为奇数的时候,我们通常倾向于给左子树分配多一个节点。

可以看出,父节点的值等于其左子树和右子树的和


那么针对这样一个结构,我们那两个问题的表现如何呢?

  • 针对问题一,求解的时候如果遇到刚好是这个结点的值的时候,直接返回即可,如果遇到有部分重合的地方,只需要更深层的遍历即可找到结果,总体的时间复杂度为O(logn)
  • 针对问题二,在这个结构中,单个元素总是叶子节点,在更新某个元素的时候,只需要沿着叶子节点向上一路更新到根节点即可。时间复杂度同样是O(logn)

这样我们就找到了让两个需求相对平均一点的方案,将这个问题整体的时间复杂度从O(n)降低到了O(logn)。

这就是线段树,线段树不仅可以应用在求和这一个问题上,涉及到整体与个体的关系的结构中,我们都可以考虑使用线段树来求解。这类的问题的特点一般是往往修改整体中的某一个元素,从结果层面上看,会对整体进行修改,但是维护这样的修改需要耗费很多的时间,我们就可以让整体的这个小变化尽可能的不影响到其他小整体,进而降低时间复杂度,这本质上是一种分治。

下一节中,我会介绍线段树在C语言中的几种常见实现方式。

  • 觉得我对你有帮助的话,动动小手点个赞再走呗~~
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值