2019独角兽企业重金招聘Python工程师标准>>>

问题

RMQ问题是求给定区间中的最值问题。对于长度为n的数列A,回答若干查询RMQ(A, i, j)。返回数组A中下标在[i,j]里的最小值的下标。比如数列 5,8,1,3,6,4,9,5,7      那么RMQ(2,4) = 3, RMQ(6,9) = 6.

解决方法

主要方法及复杂度(处理复杂度和查询复杂度)如下:

  1. 朴素(即搜索) O(n)-O(n)

  2. ST(实质是动态规划) O(nlogn)-O(1)

  3. 线段树(segment tree) O(n)-O(qlogn)

朴素

即是直接搜索,对被查询的空间进行直接遍历,时间复杂度为O(n)

ST

Sparse Table,它是一种动态规划的方法。 
    以最小值为例。a为所寻找的数组. 用一个二维数组f(i,j)记录区间[i,i+2^j-1](持续2^j个)区间中的最小值。其中f[i,0] = a[i]; 所以,对于任意的一组(i,j),f(i,j) = min{f(i,j-1),f(i+2^(j-1),j-1)}来使用动态规划计算出来。 这个算法的高明之处不是在于这个动态规划的建立,而是它的查询:它的查询效率是O(1). 
     假设我们要求区间[m,n]中a的最小值,找到一个数k使得2^k<n-m+1. 这样,可以把这个区间分成两个部分:[m,m+2^k-1]和[n-2^k+1,n].我们发现,这两个区间是已经初始化好的. 
前面的区间是f(m,k),后面的区间是f(n-2^k+1,k). 
     这样,只要看这两个区间的最小值,就可以知道整个区间的最小值!

伪代码:

//初始化INIT_RMQ//max[i][j]中存的是从i开始的2^j个数据中的最大值,最小值类似,num中存有数组的值for i : 1 to nmax[i][0] = num[i]for j : 1 to log(n)/log(2)for i : 1 to (n+1-2^i)max[i][j] = MAX(max[i][j-1], max[i+2^(j-1)][j-1])//查询RMQ(i, j)k = log(j-i+1) / log(2)return MAX(max[i][k], max[j-2^k+1][k])

C++模板:

/*** @brief sparse algorithm* @author xiyan* @date 2014/6/17* @last edit**/
#include <cstdlib>
#include <iostream>
#include <cmath>
typedef unsigned int size_t;
typedef int ssize_t;
namespace rmq{
using namespace std;
template<typename T>
class sparseTable{
public:
ssize_t createSt(const T *arrayPtr, const ssize_t arraySize);
/*build st by input*/
const T *searchSt(const ssize_t startPos, const ssize_t endPos);
/*lookup the min val from  startPos to endPos*/
virtual ~sparseTable(void);
/*destory st when class destory*/
virtual void debug(void);
private:ssize_t allocSt(const ssize_t arraySize);               /*alloc space for st*/ssize_t initSt (const T *arrayPtr);                     /*init St*/ssize_t stLog(ssize_t size) const;                      /*make lg(size)*/   T * getItem(const ssize_t base, const ssize_t logTots); /*get item from sparse table*/                        void destorySt(void);ssize_t *getMaxLogTots(void){                           return &tot_row;}               ssize_t *getMaxBase(void){return &tot_col;}T *dpSt;                                                   /*sparse table*/ssize_t tot_row;                                           /*sparee table tot row*/ssize_t tot_col;                                           /*sparse table tot col */
};
/*** @brief deinit api for st* @note call destorySt to do clean task*/
template<typename T>
void sparseTable<T>::debug(void){cout << "tot nums(lg):"<< *getMaxLogTots() << endl;cout << "tot base    :"<< *getMaxBase() << endl;if(dpSt){for(ssize_t tot = 0; tot < *getMaxLogTots(); tot++)for(ssize_t base = 0; base < *getMaxBase(); base++){cout << "Logtot " << tot;cout << ",";cout << "base " << base;cout << "| ->";cout << *getItem(base, tot) << endl;}}
}
/*** @brief deinit api for st* @note call destorySt to do clean task*/
template<typename T>
void sparseTable<T>::destorySt(void){delete dpSt;
}
/*** @brief play as a cleaner when destory st**/
template<typename T>
sparseTable<T>::~sparseTable(void){destorySt();
}
/*** @brief 2^n <= size, return n; * @return n success, -1 fail**/
template<typename T>
ssize_t sparseTable<T>::stLog(const ssize_t size) const {ssize_t ans  = 0;if(size <= 0){return -1;}while( (1 << ans) <= size){++ans;}ans--;return ans;
}
/*** @brief create sparse Table* @param[in] arrayPtr base data store in array for building sparse table* @param[in] data tots* @return 0 success, -1 fail*/
template<typename T>
ssize_t sparseTable<T>::allocSt(const ssize_t arraySize){ssize_t *maxBase    = getMaxBase();ssize_t *maxLogTots = getMaxLogTots(); *maxBase     = arraySize;*maxLogTots  = stLog(arraySize);if( *maxLogTots < 0){return -1;}*maxLogTots += 1;ssize_t totSize = (*maxBase) * (*maxLogTots);dpSt = new T[totSize];if(NULL == dpSt){return -1;}return 0;
}/*** @brief get Item from table * @note *      1. row act as totnums cnt*      2. col act as idx for base num*      3. col >= row for Table */
template<typename T>
T *sparseTable<T>::getItem(const ssize_t base, const ssize_t logTots){if( !dpSt || base < 0 || logTots < 0 || base >= *getMaxBase() || logTots >= *getMaxLogTots()){return NULL;}return (&dpSt[logTots * (*getMaxBase()) + base]);
}
/*** @brief init sparse Table by input * @param[in] arrayPtr ptr to the imput array*/
template<typename T>
ssize_t sparseTable<T>::initSt(const T *arrayPtr){for(ssize_t base = 0; base < *getMaxBase(); base++){            T  * itemPtr = getItem(base, 0);if(NULL == itemPtr){return -1;}*itemPtr = arrayPtr[base];}
#if 0cout << "init phase0 success" << endl;
#endiffor(ssize_t logTots = 1; logTots < *getMaxLogTots(); logTots++){for(ssize_t base = 0; ((base + (1 <<  logTots) ) <= (*getMaxBase())); base++){  T *lItem = getItem(base, logTots - 1); T *rItem = getItem(base + (1 << (logTots - 1)), logTots - 1);T *cItem = getItem(base, logTots);if(NULL == lItem || NULL == rItem|| NULL == cItem){return -1;}*cItem = (*lItem < *rItem ? *lItem : *rItem);}}
#if 0cout << "init phase1 success" << endl;
#endifreturn 0;
}
/*** @brief create and init sparse table* @param[in] arrayPtr ptr to the input array* @param[in] arrSize  tot nums of input**/
template<typename T>
ssize_t sparseTable<T>::createSt(const T *arrayPtr, const ssize_t arraySize){
/*build st by input*/if(allocSt(arraySize)  < 0){cout << "alloc sparse table fail" << endl;return -1;}if(initSt(arrayPtr) < 0){destorySt();cout << "init sparse table fail" << endl;return -1;}return 0;
}/*** @brief search the min num* @param[in] arrayPtr ptr to the input array* @param[in] arrSize  tot nums of input**/
template<typename T>
const T * sparseTable<T>::searchSt(const ssize_t startPos, ssize_t endPos){   ssize_t logPos;if(startPos < 0 || endPos < 0|| startPos >= *getMaxBase() || endPos >= *getMaxBase()|| startPos > endPos){return NULL;}logPos = stLog(endPos - startPos +  1);if(logPos < 0){return NULL;}T *lItem = getItem(startPos, logPos); T *rItem = getItem(endPos - (1 << logPos) + 1, logPos);if(NULL == lItem || NULL == rItem){return NULL;}return (*lItem < *rItem ? lItem : rItem);
}
}//?end of sparse table?

线段树

线段树能在对数时间内在数组区间上进行更新与查询。 定义线段树在区间[i, j] 上如下: 
第一个节点维护着区间 [i, j] 的信息。 
if i<j , 那么左孩子维护着区间[i, (i+j)/2] 的信息,右孩子维护着区间[(i+j)/2+1, j] 的信息。 
可知 N  个元素的线段树的高度 为 [logN] + 1(只有根节点的树高度为0) .

下面是区间 [0, 9]  的一个线段树:

线段树和堆有一样的结构, 因此如果一个节点编号为 x ,那么左孩子编号为2*x   右孩子编号为2*x+1.

使用线段树解决RMQ问题,关键维护一个数组M[num],num=2^(线段树高度+1). 
M[i]:维护着被分配给该节点(编号:i 线段树根节点编号:1)的区间的最小值元素的下标。 该数组初始状态为-1.

/*** @brief           segment stree* @author          xiyan* @date            2014/6/17* @last*/
#include <iostream>
#include <cstdlib>
#include <cstring>
namespace rmq
{
using namespace std;
typedef unsigned int size_t;
typedef signed   int ssize_t;
static ssize_t inline  getLftNode(const ssize_t idx)
{return (idx << 1);
}
static ssize_t inline  getRhtNode(const ssize_t idx)
{return ((idx << 1) + 1);
}
template<typename T>
class segmentTree
{
public:segmentTree(void):deps(0), nodes(0), elems(0), tree(NULL) {}ssize_t createSt(const T *input, const ssize_t cnt);~segmentTree(void){destorySt();}const T * searchSt(const ssize_t lftQuery, const ssize_t rhtQuery);virtual void debug(void);
private:ssize_t allocSt(const ssize_t cnt);ssize_t initSt(const T *input);ssize_t initNode(const ssize_t nodeIdx, const ssize_t lftIdx, const ssize_t rhtIdx, const T *input);const T *searchNode(const ssize_t nodeIdx, const ssize_t lftIdx, const ssize_t rhtIdx, \const ssize_t lftQuery, const ssize_t rhtQuery);void destorySt(void);ssize_t log(const ssize_t x) const;bool checkPow(const ssize_t x) const;T *tree;ssize_t deps;                                 /*dep of the tree*/ssize_t nodes;                                /*nodes tots*/ssize_t elems;                                /*element tots*/
};
/*** @brief 2^n <= x, return n;* @return n success, -1 fail**/
template<typename T>
ssize_t segmentTree<T>::log(const  ssize_t x) const
{ssize_t ans  = 0;ssize_t cnt = x;if(cnt <= 0){return -1;}while( (1 << ans) <= cnt){++ans;}ans--;return ans;
}
/*** @brief make sure the input is pow of 2* @return n success, -1 fail**/
template<typename T>
bool segmentTree<T>::checkPow(const  ssize_t x) const
{if(x <= 0){return false;   /*x <= 0*/}if(!(x & (x - 1))){return true;    /*x == 2^n*/}else{return false;   /*x > 0 && x != 2^n*/}
}
/*** @brief get the dep and the size for tree* @param[in] cnt tots of the input to build tree**/
template<typename T>
ssize_t segmentTree<T>::allocSt(const ssize_t cnt)
{if(cnt <= 0){return -1;}ssize_t depTmp = log(cnt);if(depTmp < 0){return -1;}if(!checkPow(cnt)){depTmp++;}depTmp++;ssize_t nodeTots = (1 << depTmp) - 1;   /*nodes needed */nodeTots++;                             /*add empty node at head*/tree = new T[nodeTots];if(!tree){return -1;}memset(tree, 0, nodeTots);deps   = depTmp;                      /*store cnt*/nodes  = nodeTots;elems  = cnt;return 0;
};
/*** @brief init tree**/
template<typename T>
ssize_t segmentTree<T>::initSt(const T *input)
{const ssize_t rootNode = 1;const ssize_t lftIdx   = 0;const ssize_t rhtIdx   = elems - 1;if(initNode(rootNode, lftIdx, rhtIdx, input) < 0){return -1;}return 0;
}
/*** @brief init node and continue to build children* @param[in] nodeIdx curr node* @param[in] lftIdx start idx for input* @param[in] rhtIdx end idx  for input* @param[in] store for input* @return -1 error*         0  success*/
template<typename T>
ssize_t segmentTree<T>::initNode(const ssize_t nodeIdx, const ssize_t lftIdx, const ssize_t rhtIdx, const T *input)
{//cout << "nodeIdx = " << nodeIdx << "," << "nodes =" << nodes << endl;  cout << lftIdx << " "<< rhtIdx << endl;if(nodeIdx >= nodes)     /*segment error*/{return -1;}if(lftIdx == rhtIdx){tree[nodeIdx] = input[lftIdx];return 0;}ssize_t midIdx = (lftIdx + rhtIdx) >> 1;ssize_t curNode = nodeIdx;ssize_t lftNode = getLftNode(curNode);ssize_t rhtNode = getRhtNode(curNode);if(initNode(lftNode, lftIdx, midIdx, input) < 0){return -1;}if(initNode(rhtNode, midIdx + 1, rhtIdx, input) < 0){return -1;}tree[curNode] = (tree[lftNode] < tree[rhtNode] ? tree[lftNode] : tree[rhtNode]);return 0;
}
/*** @brief create segment tree* @param[in] input elems used to build segment tree* @param[in] cnt   tot nums of elems* @return 0 success, -1 fail*/
template<typename T>
ssize_t segmentTree<T>::createSt(const T *input, const ssize_t cnt)
{if(allocSt(cnt) < 0){cout << "alloc space for segment tree fail" << endl;return -1;}if(initSt(input) < 0){cout << "init segment tree fail" << endl;return -1;}return 0;
}
/*** @brief search ans for the query range* @param[in] nodeIdx node index* @param[in] lftIdx  lft index for data store* @param[in] rhtIdx  rht index for data store* @param[in] lftQuery lft for query range* @param[in] rhtQuery rht for query range*/
template<typename T>
const T * segmentTree<T>::searchNode(const ssize_t nodeIdx, const ssize_t lftIdx, const ssize_t rhtIdx, \const ssize_t lftQuery, const ssize_t rhtQuery)
{if(lftIdx > rhtQuery || rhtIdx < lftQuery){return NULL;}if( lftQuery <= lftIdx && rhtQuery >= rhtIdx){return (&tree[nodeIdx]);}ssize_t midIdx = (lftIdx + rhtIdx) >> 1;ssize_t curNode = nodeIdx;ssize_t lftNode = getLftNode(curNode);ssize_t rhtNode = getRhtNode(curNode);const T  *lftans = searchNode(lftNode, lftIdx, midIdx, lftQuery, rhtQuery);const T  *rhtans = searchNode(rhtNode, midIdx + 1, rhtIdx, lftQuery, rhtQuery);if(!lftans){return rhtans;}if(!rhtans){return lftans;}return (*lftans  < *rhtans ? lftans : rhtans);
}
template<typename T>
const T * segmentTree<T>::searchSt(const ssize_t lftQuery, const ssize_t rhtQuery)
{if(lftQuery < 0 || rhtQuery >= elems || lftQuery > rhtQuery){return NULL;}const T *ans;const ssize_t rootNode = 1;const ssize_t lftIdx   = 0;const ssize_t rhtIdx   = elems - 1;if( NULL == (ans = searchNode(rootNode, lftIdx, rhtIdx, lftQuery, rhtQuery))){return NULL;}return  ans;
}
/*** @brief destory the array for store tree**/
template<typename T>
void segmentTree<T>::destorySt(void)
{delete tree;deps = 0;nodes = 0;elems = 0;
}
/*** @brief debug segment tree**/
template<typename T>
void segmentTree<T>::debug(void)
{cout << "deps :  " << deps  << endl;cout << "nodes:  " << nodes << endl;cout << "elems:  " << elems << endl;
}
}

参考文章

  1. http://blog.csdn.net/huangxy10/article/details/7945856

  2. http://blog.163.com/zhaohai_1988/blog/static/209510085201263011135062/

转载于:https://my.oschina.net/u/572632/blog/280347

RMQ(Range Minimum Query)相关推荐

  1. Vue路由传参详解(params 与 query)

    Vue路由传参详解(params 与 query) 前言: 路由传参分为 params 传参与 query 传参 params 传参类似于网络请求中的 post 请求,params 传过去的参数不会显 ...

  2. Vue路由传参(params 与 query)

    理解: 路由传参分为 params 传参与 query 传参,params 传参类似于网络请求中的 post 请求,params 传过去的参数不会显示在地址栏中(但是不能刷新).params 只能配合 ...

  3. C# 语言集成查询 LINQ(Language Integrated Query)

    提示:如有错误,请不吝指出 文章目录 注意 一.首先声明两个示例集合 二.LINQ多种操作方法 1.Select方法返回基于某具体类型的可枚举集合(指定的所有数据) 2.枚举器返回多个数据项 3.筛选 ...

  4. java term_[ElasticSearch]Java API 之 词条查询(Term Level Query)

    1. 词条查询(Term Query) 词条查询是ElasticSearch的一个简单查询.它仅匹配在给定字段中含有该词条的文档,而且是确切的.未经分析的词条.term 查询 会查找我们设定的准确值. ...

  5. C#LeetCode刷题之#598-范围求和 II​​​​​​​(Range Addition II)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3881 访问. 给定一个初始元素全部为 0,大小为 m*n 的矩阵 ...

  6. python xrange_Python学习中的知识点(range和xrange)

    range() 如果需要迭代一个数字序列的话,可以使用range()函数,range()函数可以生成等差级数. 如例: for i in range(5) print(i) 这段代码将输出0, 1, ...

  7. vue-router路由参数(params和query)

    ⼀.params和query params⽅法传参的时候,要在路由后⾯加参数名占位:并且传参的时候,参数名要跟路由后⾯设置的参数名对应. /user/:id这个路由匹配/user/111, /user ...

  8. RMQ(求区间最值问题)

    学习博客:https://blog.csdn.net/qq_31759205/article/details/75008659 RMQ(Range Minimum/Maximum Query),即区间 ...

  9. 从零开始的LCA(最近公共祖先)

    清明在机房听学长讲树上差分时提到了 \(LCA\) 这个东西,就顺带着学习了一波.由于本人是个蒟蒻,为了避免出现看不懂自己写的 \(Blog\) 这种情况,文中涉及到的知识和概念都会 概括性地 讲一下 ...

最新文章

  1. YOLOV5 的小目标检测网络结构优化方法汇总(附代码)
  2. 帮初学者快速上手机器学习,这有一份Colab资源大全
  3. 阿里巴巴 Kubernetes 能力再获 CNCF 认可 | 云原生生态周报 Vol. 32
  4. html相同标签nth,详解CSS nth-child与nth-of-type的元素查找方式
  5. USBASP的ISP上位机软件AVR_fighter
  6. ubuntu nfs
  7. 0330Cache Buffers chains与共享模式疑问
  8. 平衡二叉搜索树的创建
  9. 使用easyexcel导出时行高不自动调整的解决
  10. 错误: 代理抛出异常错误: java.rmi.server.ExportException: Port already in use: 1099; nested exception is
  11. 1061. Dating (20)-PAT甲级真题
  12. 按位与、按位或、按位异或、左移、右移运算符的简单介绍(部分二进制运算符的简单介绍)...
  13. python虚拟环境中安装diango_django是要在虚拟环境激活后安装吗
  14. Node.js的安装下载和运行JS代码和常用命令和按键
  15. vue引入自定义字体otf、ttf字体的方法
  16. Android开发入门教程pdf
  17. java与VUE有啥不同_React的世界观及与Vue之比较
  18. 计算机爱情诗,优美诗句大全
  19. 因式分解用python写程序_使用Python实现质因式分解算法
  20. 内存占用少的linux桌面,不同桌面环境占用内存/CPU对比

热门文章

  1. android指定sqlite路径_Android:自定义Sqlite数据库路径
  2. 多线程测试工具groboutils的使用
  3. Knockout 官网学习文档目录
  4. mac下shell给文件名批量加前缀
  5. js中闭包的概念和用法
  6. 自定义的Sort对象
  7. Asp.net動態添加控件(转)
  8. Android 内容提供者(Content provider)
  9. 数据段、代码段、堆栈段、BSS段的区别
  10. 利用python中的xlrd和xlwt操作excel