RMQ(Range Minimum Query)
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.
解決方法
主要方法及復雜度(處理復雜度和查詢復雜度)如下:?
樸素(即搜索) O(n)-O(n)?
ST(實質是動態規劃) O(nlogn)-O(1)?
線段樹(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.?
?
參考文章
?
http://blog.csdn.net/huangxy10/article/details/7945856
http://blog.163.com/zhaohai_1988/blog/static/209510085201263011135062/
轉載于:https://my.oschina.net/u/572632/blog/280347
總結
以上是生活随笔為你收集整理的RMQ(Range Minimum Query)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Hashtable输出J
- 下一篇: 重力感应机制和手机的屏幕绘画