RMQ问题第一弹
今天,我给大家分享一下我在学习 RMQ 问题过程中对该问题的理解。
RMQ (Range Minimum/Maximum Query ):中文名为“区间最值查询”。RMQ 问题指的是给定一段区间,针对给定区间进行若干次查询,每次给出不同的待查询子区间范围,要求返回子区间内的最大值或者最小值。
一般此类问题会给出区间内总元素个数 n 、待查询的次数 q ,以及每次查询的始末位置。
根据常规的思路,要找出一段区间中的最大值或最小值,我们会采取遍历区间的做法,该方法的核心代码在此不作展示。分析可知,n 个数据的规模,进行 q 次查询,时间复杂度为 o(q*n ),当问题的规模逐渐增大或者查询的次数逐渐增多的时候,我们无法在有限的时间内完成任务。因此,寻找更方便更快捷的办法成为我们的目标。在学习算法的道路上,我们都知道,第一直觉想出来的解决思路往往是最直接也是最低效的,于是理所当然的,我们需要对原来的思路进行改进,甚至摒弃原来低效的方法另辟蹊径。常规的思路时间主要耗费在对最值的查询和求解工作上,我们可以想办法将查询时间缩短,有人提出将可能查询的区间列出来,针对每个区间找出其中的最值存储在数组里,每次查询最值数组即可,这个思路需要存储的量有三个,起始点、终止点、区间最值,可以考虑模仿动态规划的理念,对二维数组的两个下标和存储的值赋予特殊含义,即用二维数组的第一坐标表示起始点、第二坐标表示终止点,那么值就可以表示区间最值。这样一来,查询的时间缩短为 o(1),但是问题在于,该 n* n 的二维数组赋值的时候,如果采用遍历对应区间的方法求最值,整个二维数组赋值过程时间复杂度为 o(n^3),这就意味着,虽然做了多余的工作,也做了合理的设想,但是并未达到优化问题的目的。这是不是意味着当前的思路不可行呢?我们需要先考虑该二维数组赋值过程能不能优化,如果不能,那这个思路就可以宣布失败了。
天无绝人之路,机会总是偏爱大胆实践的人。我们可以在此作出大胆预测,如果采用以上对二维数组的定义:第一坐标表示起始点,第二坐标表示终止点,值表示最值,即使求解最值的方法时间复杂度是 o(1),那求解该二维数组的整个过程时间复杂度也在 o(n^2),我们知道并没有 o(1)求解乱序区间最值的方法,因此,可以得出结论,以上对二维数组的定义无法达到优化的目的。
这就意味着问题不仅出现在求解区间最值的方法上,同时基本的二维数组定义也是不合理的。不合理之处在于规模过大,我们需要想办法缩小二维数组的规模。表示某一区间和其最值除了首末端点和最值三个参数这种方法,还可以用区间起始点、区间长度、区间最值三个参数。区间长度范围是 1 ~ n ,换个思路想一下,表示区间长度时候定义可以是多样的。
由此衍生出第二种方法-- ST 方法。
ST(Sparse Table)方法,sparse 中文译为:稀疏的,稀少的,零星的;table 中文译为:桌子,表,目录,手术台,工作台,游戏台;Sparse Table 具体中文名到底如何翻译,没有一个固定说法,我们暂且称之为 ST方法,该方法的本质是用动态规划方法求出用于查询的最值数组,以待查询子区间的起始位置和区间长度标记一个状态,以该子区间内的最大值或最小值作为状态值。
以求最大值为例: dp[ i ][ j ] = max ,其中,i 表示待查询子区间起始位置 ;j 表示待查询子区间的长度为 2 ^ j , max 表示在该子区间中最大值为 max 。 分析可知:1 <= i <= n , 0 <= j <= log n (以 2 为底), 此时,二维数组的规模为 n *log n (以 2 为底) 。动态规划的表达式有了,现在我们来研究状态转换方程,由 dp 数组的定义可以看出来,dp[ i ][ j ] 表示的区间长度 2 ^ j 一定是偶数,因此,区间可以等分为两个长度为 2 ^( j - 1 ) 的子区间,依照分治的思想,求该区间的最值,可以通过选择两个子区间最值中的最值来实现。此时,状态转换方程为:
dp[ i ] [ j ] = max ( dp[ i ][ j - 1 ] , dp[ i + 2^( j - 1 ) - 1][ j - 1 ] )
求最值数组具体的核心代码实现过程如下:
注意,第 13 行的注释“ j 必须为外层循环控制量”,原因在于求解过程中,先求出以各元素为初始元素区间长度为 1 的最值,再求出以各元素为初始元素区间长度为 2 的最值,依次求解。因此 i 为内层循环控制量, j 为外层循环控制量,而不能以 i 为外层循环控制量,因为此时假设 求 dp[ i ] [ j ] ,如果 i 为外层循环控制量,dp[i][j] = max( dp[i][j-1] , dp[i + 2 ^(j-1) - 1][j-1]) ,其中 dp[ i ][ j - 1] 已知 ,但是 dp[i + 2 ^(j-1) - 1][j-1] 未知的,准确说, 第一坐标值 大于 i 的值都是未知的。
查询的时候,已知的信息是始末元素下标 start 和 end ,我们的 dp 数组中存储的是长度为 2 的幂的区间最值,对于长度非 2 的幂的区间,我们需要找到能够覆盖这个区间的两个区间,选取两者的最值即可。
如下,求下标 1 ~12 的元素中的最值,log 12 (以 2 为底)= 3 ,因此选取 F(1,3)和 F(5,3)两个区间,组合起来可以覆盖待求区间的所有数据。
查询部分核心代码如下:
今天的分享就到这里,其他相关的方法且看下篇文章。
感谢大家对我的支持,没关注的朋友可以扫下方二维码关注我呦。
RMQ问题第一弹的更多相关文章
- RMQ_第一弹_Sparse Table
title: RMQ_第一弹_Sparse Table date: 2018-09-21 21:33:45 tags: acm RMQ ST dp 数据结构 算法 categories: ACM 概述 ...
- typecho流程原理和插件机制浅析(第一弹)
typecho流程原理和插件机制浅析(第一弹) 兜兜 393 2014年03月28日 发布 推荐 5 推荐 收藏 24 收藏,3.5k 浏览 虽然新版本0.9在多次跳票后终于发布了,在漫长的等待里始终 ...
- 线段树+RMQ问题第二弹
线段树+RMQ问题第二弹 上篇文章讲到了基于Sparse Table 解决 RMQ 问题,不知道大家还有没有印象,今天我们会从线段树的方法对 RMQ 问题再一次讨论. 正式介绍今天解决 RMQ 问题的 ...
- 我的长大app开发教程第一弹:Fragment布局
在接下来的一段时间里我会发布一个相对连续的Android教程,这个教程会讲述我是如何从零开始开发“我的长大”这个Android应用. 在开始之前,我先来介绍一下“我的长大”:这是一个校园社交app,准 ...
- Hadoop基础-MapReduce的工作原理第一弹
Hadoop基础-MapReduce的工作原理第一弹 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在本篇博客中,我们将深入学习Hadoop中的MapReduce工作机制,这些知识 ...
- Java基础-程序流程控制第一弹(分支结构/选择结构)
Java基础-程序流程控制第一弹(分支结构/选择结构) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.if语句 1>.if语句的第一种格式 if(条件表达式){ 语句体: ...
- codechef 营养题 第一弹
第一弾が始まる! 定期更新しない! 来源:http://wenku.baidu.com/link?url=XOJLwfgMsZp_9nhAK15591XFRgZl7f7_x7wtZ5_3T2peHh5 ...
- 好玩的WPF第一弹:窗口抖动+边框阴影效果+倒计时显示文字
原文:好玩的WPF第一弹:窗口抖动+边框阴影效果+倒计时显示文字 版权声明:转载请联系本人,感谢配合!本站地址:http://blog.csdn.net/nomasp https://blog.csd ...
- [Git] 002 初识 Git 与 GitHub 之加入文件 第一弹
在 GitHub 的 UI 界面使用 Git 往仓库里加文件 第一弹 1. 点击右上方的 Create new file 2. 在左上方填入文件名,若有后缀,记得加上 3. 页面跳转,此时已有两个文件 ...
随机推荐
- JavaScript 学习笔记 - Web Workers
前言 本文仅是 Web Workers 的入门科普文章,不涉及太琐碎的知识点. 我们知道,在 Web Workers 出来之前,JavaScript 是单线程的.即使是 setTimeout 之类的看 ...
- PyQt4 初试牛刀二
一.最小话托盘后,调用showNormal()后窗口不刷新,解决办法如下: 重写showNormal 方法,调用父类方法后,repaint窗体 def showNormal(self): su ...
- [读书笔记] 四、SpringBoot中使用JPA 进行快速CRUD操作
通过Spring提供的JPA Hibernate实现,进行快速CRUD操作的一个栗子~. 视图用到了SpringBoot推荐的thymeleaf来解析,数据库使用的Mysql,代码详细我会贴在下面文章 ...
- webSocket浏览器握手不成功(解决)
websocket与服务端握手会报握手不成功的错误解决方法: 首先是服务端首次收到请求要回报给客户端的报文要做处理多的不说,方法敬上: /// <summary> /// 打包请求连接数据 ...
- clob字段的值插入和查询N种方法【包括java调用存储过程传入clob参数】
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import jav ...
- Ext:添加进度条
var myMask = new Ext.LoadMask(Ext.getBody(), {msg:"正在提交..."}); myMask.show(); myMask.hide( ...
- php工具方法
备忘常用方法 1.寻找子栏目(权限\菜单列表...) function getSon($list,$pid){ $arr=''; foreach ($list as $k=>$v){ if($v ...
- quartz源码分析——执行引擎和线程模型
title: quartz源码分析--执行引擎和线程模型 date: 2017-09-09 23:14:48 categories: quartz tags: [quartz, 源码分析] --- - ...
- WPF-TreeView获取文件夹目录、DataGrid获取目录下文件信息
开发一个WPF桌面应用程序.刚接触WPF编程以及C#语言,这里把一些关键的问题记录下来. 下面是实现将路径的文件夹信息绑定到TreeView及DataGrid上显示. 关键问题是C#数据绑定方式及IE ...
- 结对编程1---基于Flask的四则运算题目生成器
项目代码地址 / WEB应用地址 / 合作伙伴iFurySt博文链接 需求分析 本次程序是基于原有的控制台四则运算器的基础上,改成WEB的形式,同时还增加了一些新的功能.同时因为交互方式的改变,代码也 ...