线段树+RMQ问题第二弹

上篇文章讲到了基于Sparse Table 解决 RMQ 问题,不知道大家还有没有印象,今天我们会从线段树的方法对 RMQ 问题再一次讨论。

正式介绍今天解决 RMQ 问题的方法之前,我先对 RMQ 问题的概念再一次进行说明。RMQ (Range Minimum/Maximum Query ):中文名为“区间最值查询”。RMQ 问题指的是给定一段区间,针对给定区间进行若干次查询,每次给出不同的待查询子区间范围,要求返回子区间内的最大值或者最小值。

RMQ 问题可以看作是线段树的一个应用,本来今天的主角是 RMQ 问题,但我思前想后觉得草草解释线段树或者只展示RMQ一个方面的应用留个小尾巴有点意犹未尽,之前没有接触过线段树的朋友也会觉得云里雾里,因此我改变主意决定以线段树的基本概念和应用作为今天的主要内容,RMQ问题的解决作为整篇文章的一个实例,下面就要进入主题了。

线段树是什么?线段树是一种完美的树,这种树对于处理区间问题有相当高的效率。线段树满足这样一些特点:

一、是一棵完全二叉树,除最后一层之外其余各层均是满的,所有的叶节点均排布在最后一层,所有的节点不是叶节点就是拥有两棵子树的节点。

二、每个节点维护一段区间,其中根节点维护的是整个区间,其余节点均维护的是直接父节点的二分之一区间,也就意味着将父节点维护的区间二等分分别分给左右两个孩子维护。

三、根据节点维护的数据不同,线段树可以实现不同的功能。

四、并非所有的区间问题都可以用线段树来解决。

这样不形象的用文字描述就想让没接触过的人了解线段树实在有点强人所难,我还是画一张丑图来说明一下吧。如下图所示:

以求 RMQ 问题为例我们来深入了解一下有关线段树的性质,假设本篇文章我们的要求是求区间最小值。

由上图可知,该图中的线段树维护的是 n = 8 的区间的数据,每个节点存储的是所维护区间内的最小值,该线段树的深度为 L 。通常情况下整个线段树各节点的值会存储在数组里边,因此我们需要知道线段树的节点数。假设 区间的元素个数为 n ,线段树的深度为 L ,线段树的节点数为 m 。

由上图可知三者满足的关系为:

L = log n +1(对数以 2 为底);即 2^(L-1) = n

m = 2^0 + 2^1 + 2^2 + 2^3 + ......+ 2^( L - 1)

m =( 2^0 + 2^0 )+ 2^1 + 2^2 + 2^3 +...... + 2^( L-1) -1

m = (2^1 + 2^1) + 2^2 + 2^3 +......+ 2^(L-1) - 1

... ...

m = 2^(L-1) + 2^(L-1) -1

m = n + n -1

m = 2n-1

由上面的推导过程可以知道,m = 2n-1是妥妥的。没求证之前,可能很多人看到线段树的图之后,都会觉得规模应该是 O(n log n),虽然不知道这种错觉的理由是什么,但是知道了一点,以后凡事都需要自己亲自验证一番才能彻底打消错误的念头。这也就意味着对线段树的初始化是O(n)的时间复杂度。

现在我们来看一下求区间最值具体是怎样实现的。首先我们知道线段树上存储的是一些固定区间的最值,因此我们要求一些自定义区间的最值时,需要同时结合线段树上的若干个连续区间的多个最值,在其中选择最小的作为最后的结果,由于将整个区间分成若干子区间分别求最值最后合并结果的方法对最后结果的正确性没有影响,因此存在实现上的可行性。那么对于自定义的一个区间,在线段树上具体需要选取哪些区间呢?我们需要选取的是能够完全被自定义区间覆盖的那些区间。对于线段树上的任何一个区间,可能执行的操作有三个:

若该区间中的所有元素均包含在自定义区间中,那么直接返回线段树上存储的该区间最值。

若该区间的所有元素中任何一个元素都不曾出现在自定义区间中,那么自定义区间的最值一定不会来自该区间,这时候返回一个不影响结果判断的值即可。

若该区间既不能被自定义区间完全包含,又存在若干元素包含在自定义区间中,那么递归的对该区间对应的两个子区间分别执行这三个判断。

大概思路就是如此。

虽然将普通区间改造为线段树来维护没有增加过多的任务量,由上面的分析可以知道这么做的效果却是很惊人的,因为利用线段树求区间最值的时候,在树的每一层至多需要访问两个节点,由上至下共 L = log n +1层,也就意味着,找到一个区间的最值需要访问的节点数 至多都不会超过 2 * L - 1 ,因此查找一次区间最值时间复杂度稳定在 O( log n)的时间复杂度,这可是个惊人的发现呢。

线段树的另一个比较有名的应用就是修改区间上某一位置的值,可以从线段树的最底层开始修改,由于在任何一层每个元素只可能出现在一个区间,因此修改一个值的时间复杂度恒为O(log n)。

线段树的两个比较常见的应用都在这里,理论描述上面已经给出,接下来我将核心代码部分附上供大家对细节进行推敲。图片看不清楚的朋友可打开网页http://paste.ubuntu.com/25418076/查看网页版的代码。

今天的分享就到这里,有朋友发现文章中写的不合适的地方可以直接在下方留言区留言纠正。

没有关注公众号的朋友,可以识别下方二维码关注我。


线段树+RMQ问题第二弹的更多相关文章

  1. HDU 1754 I Hate It 线段树RMQ

    I Hate It Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=175 ...

  2. 《白书》上线段树RMQ的实现

    白书上的线段树RMQ实现,自己重写了一遍: #include <bits/stdc++.h> using namespace std; const int MAXN=1<<17 ...

  3. ACM学习历程—HDU 5289 Assignment(线段树 || RMQ || 单调队列)

    Problem Description Tom owns a company and he is the boss. There are n staffs which are numbered fro ...

  4. UVA 11235 Frequent values 线段树/RMQ

    vjudge 上题目链接:UVA 11235 *******************************************************大白书上解释**************** ...

  5. dutacm.club_1094_等差区间_(线段树)(RMQ算法)

    1094: 等差区间 Time Limit:5000/3000 MS (Java/Others)   Memory Limit:163840/131072 KB (Java/Others)Total ...

  6. Hlg 1832 【线段树 && RMQ】.cpp

    题意: 在给出的区间内求出最大买进卖出的差价. 思路: 对于弱数据:维护一个从左到右的最大差价和最小值.即当发现当前值比最小值小的时候更新最小值,否则看一下当前值与之前最小值的差价是否比最大差价大,是 ...

  7. 单点更新线段树 RMQ

    D. Xenia and Bit Operations time limit per test 2 seconds memory limit per test 256 megabytes input ...

  8. CSU 1809 - Parenthesis - [前缀和+维护区间最小值][线段树/RMQ]

    题目链接:http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1809 Bobo has a balanced parenthesis sequenc ...

  9. PKU 2823 Sliding Window(线段树||RMQ||单调队列)

    题目大意:原题链接(定长区间求最值) 给定长为n的数组,求出每k个数之间的最小/大值. 解法一:线段树 segtree节点存储区间的最小/大值 Query_min(int p,int l,int r, ...

随机推荐

  1. win7怎么更换锁屏壁纸

    win7怎么更换锁屏壁纸... -------------------------- 按键盘组合键“Windows+R”可打开“运行”窗口,输入 “gpedit.msc” 按回 ----------- ...

  2. vue-cli脚手架npm相关文件解读(8)check-versions.js

    系列文章传送门: 1.build/webpack.base.conf.js 2.build/webpack.prod.conf.js 3.build/webpack.dev.conf.js 4.bui ...

  3. PYQT4 : QSystemTrayIcon练习

    照着demo自己做了一遍,练练手 import sys from PyQt4 import QtGui from PyQt4 import QtCore class SysTray(QtGui.QDi ...

  4. poj3876 darts

    Darts Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 58   Accepted: 32   Special Judge ...

  5. Servlet使用简介

    Servlet的使用基本包含三个步骤: 1.继承HttpServlet 或实现Servlet 接口 (根据源码分析最终都是对servlet接口的实现) 2.配置地址: 配置web.xml 或者用注解的 ...

  6. Express + Session 实现登录验证

    1. 写在前面 当我们登录了一个网站,在没有退出登录的情况下,我们关闭了这个网站 ,过一段时间,再次打开这个网站,依然还会是登录状态.这是因为,当我们登录了一个网站,服务器会保存我们的登录状态,直到我 ...

  7. 使用 PUTTY 操作 Google Cloud

    目的: 使用putty连接Google Cloud 实例. 总说: 首先要用  PuTTYgen生成 private key  和 public key, 之后 登录Google Cloud 将生成的 ...

  8. 边看MHA源码边学Perl语言之一开篇

    边看MHA源码边学Perl语言之一开篇 自我简介 先简单介绍一下自己,到目前为此我已经做了7年左右的JAVA和3年左右php开发与管理,做java时主要开发物流行业的相关软件,对台湾快递,国际快递,国 ...

  9. mysql创建字段非空NOT NULL的好处

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt338 很多表都包含可为 NULL (空值) 的列,即使应用程序井不需要保存 ...

  10. 深度剖析Java变量栈&对象堆

    Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...