RMQ((Range Minimum/Maximum Query))ST算法

給定一個數組,求出給定區間[l,r]中元素的最大值或最小值或者最值的索引。算法

一看到這個題目,簡單,看我暴力出奇跡。暴力固然是可行的。可是時間複雜度很高(O(n^2))。線段樹,樹狀數組也能夠解決這個問題,複雜度(O(nlogn))的預處理,最終查詢爲O(次數*logn)。數組

而今天用ST(Sparse_Tabl)算法,也是O(nlogn)的預處理,可是是單次查詢爲O(1),挺高效的。ide

咱們如今給定一組數據 n==9,元素爲2,4,6,8,9,1,2,3,4。一個二維數組f[MAX][MAX];假設咱們求區間最大值的問題。spa

f[i][j]的定義是這樣的,維護從當前i的位置開始,共2^j個元素中的最大值3d

若是f[i][0]則表示從自身到自身(2^0==1)的元素的最大值。f[i][1]表示從自身到下一個元素(2^1==2)中最大的那個。依次類推。code

可是,這個二維數組也是有範圍的。總共就n個元素,因此j的最大值爲log2(n),而i的範圍則是受j的範圍影響的。即j每次跨度越小,i的範圍就越大,若是j的跨度較大,i的範圍就小。blog

另外,f數組的計算過程以下索引

f[1][0]就是數組自己,f[1][1]就是本身和下一個元素的最大值,f[1][2]是本身和後三個元素中的最大值(共2^2==4個),而這個不須要從新去比較4個,而是在f[1][1](有前兩個和後兩個的最大值)的基礎上,比較2個就行了。get

這樣,每個元素日後的2^k次方的中的最大值就保存下來了。event

那麼,如何計算給定區間[i,j]中的最大值呢

上圖中,根據範圍分別算出兩個子區間的f[i][j]中的i和j,即f[i][j-1]和f[i+1<<(j-1)][j-1],子區間範圍是k==8。

那麼,兩個子區間的範圍k怎麼肯定呢?很簡單,根據i到j的範圍,k<log2(j-i+1),即不超過要求範圍[i,j]的2^k的最大的那個k。這樣,不管如何,兩個子區間都能徹底覆蓋整個所求區間,重疊了也不會影響結果。

圖中找出2^3==8< 12,子區間是重疊的,若是[i,j]是4,那麼2,2的子區間就行,這個則不重疊。

這樣,就能獲得問題的解。這個查詢時間複雜度是O(1)的,上述計算在預處理中便可完成。

 1 for(int i = 1;i <= n; i++)
 2     {
 3         f[i][0] = arr[i];//存放最大,從i開始,長度爲2^0距離的最大值爲本身
 4         v[i][0] = arr[i];//存放最小
 5     }
 6     for(int j = 1; (1<<j)<=n ;j++)//j<=m
 7     {
 8         for(int i = 1;i+(1<<j)-1 <=n ;i++)//i的範圍受j的跨度的影響
 9         {    
10             v[i][j] = min( v[i][j-1] , v[i+(1<<(j-1))][j-1]);
11             f[i][j] = max( f[i][j-1] , f[i+(1<<(j-1))][j-1]);
12         }
13     }
View Code

這個題目能夠練練手,純RMQ(線段樹固然也ok)---- 傳送門

相關文章
相關標籤/搜索