ST表類似樹狀數組,線段樹這兩種算法,是一種用於解決RMQ(Range Minimum/Maximum Query,即區間最值查詢)問題的離線算法
與線段樹相比,預處理復雜度同為O(nlogn),查詢時間上,ST表為O(1),線段樹為O(logn)
st表的主體是一個二維數組st[i][j],表示需要查詢的數組的從下標i到下標i+2^j - 1的最值,這里以最小值為例
預處理函數:
1 int a[1010];//原始輸入數組 2 int st[1010][20];//st表 3 4 void init(int n) 5 { 6 for (int i = 0; i < n; i++) 7 st[i][0] = a[i]; 8 9 for (int j = 1; (1 << j) <= n; j++) 10 { 11 for (int i = 0; i + (1 << j) - 1 < n; i++) 12 st[i][j] = min(st[i][j - 1],st[i + (1 << (j - 1))][j - 1]); 13 } 14 }
這里首先把從0~n-1的2^0部分進行覆蓋,再往下繼承
繼承這里也很好理解,我們以一個長度為5的數組[5,1,2,3,4]為例
2^0部分覆蓋過去自然是5,4,3,2,1
2^1部分的長度為4,從0一直到3,因為從下標為4開始后面只有他自己
st[0][1]是下標為0~1的最小值,自然也就是st[0][0]和st[1][0]的最值
以此往下類推我們可以得出結論:
st[i][j] = min(st[i][j - 1],st[i + 2^(j - 1))][j - 1])
到這里初始化就完成了,注意下標不要越界,如果你對為什么這么處理有困惑的話,請繼續看查詢
查詢函數這里不太好理解
初始化時,每一個狀態對應的區間長度都為2^j,由於給出的查詢區間長度不一定恰好為2^j,
所以我們要引出一個定理:2^log(a)>a/2 。
https://blog.csdn.net/Hanks_o/article/details/77547380 這里有一段非常非常好理解的解釋,這里超級感謝原作者,我本人不能做出更好的解釋,他的講解是這樣的:
這個很簡單,因為log(a)表示小於等於a的2的最大幾次方。
比如說log(4)=2,log(5)=2,log(6)=2,log(7)=2,log(8)=3,log(9)=3…….
那么我們要查詢x到y的最小值。
設len=y-x+1,t=log(len)
根據上面的定理:2^t>len/2
從位置上來說,x+2^t越過了x到y的中間!
因為位置過了一半
所以x到y的最小值可以表示為min(從x往后2^t的最小值,從y往前2^t的最小值)
前面的狀態表示為mn[t][x]
設后面(從y往前2^t的最小值)的初始位置是k,
那么k+2^t-1=y,所以k=y-2^t+1
所以后面的狀態表示為mn[t][y-2^t+1]
所以x到y的最小值表示為min(mn[t][x],mn[t][y-2^t+1]),所以查詢時間復雜度是O(1)
查詢函數:
1 int search(int l, int r) 2 { 3 int k = (int)(log((double)(r - l + 1)) / log(2.0)); 4 return min(st[l][k],st[r - (1 << k) + 1][k]); 5 }
示例程序:
1 #include <iostream> 2 #include <algorithm> 3 4 using namespace std; 5 6 int a[1010];//原始輸入數組 7 int st[1010][20];//st表 8 9 void init(int n) 10 { 11 for (int i = 0; i < n; i++) 12 st[i][0] = a[i]; 13 14 for (int i = 1; (1 << i) <= n; i++) 15 { 16 for (int j = 0; j + (1 << i) - 1 < n; j++) 17 st[j][i] = min(st[j][i - 1],st[j + (1 << (i - 1))][i - 1]); 18 } 19 } 20 21 int search(int l, int r) 22 { 23 int k = (int)(log((double)(r - l + 1)) / log(2.0)); 24 return min(st[l][k],st[r - (1 << k) + 1][k]); 25 } 26 27 int main() 28 { 29 int n,m; 30 while (cin >> n >> m) 31 { 32 for (int i = 0; i < n; i++) 33 cin >> a[i]; 34 35 init(n); 36 37 while (m--) 38 { 39 int l, r; 40 cin >> l >> r; 41 cout << search(l,r) << endl;; 42 } 43 } 44 return 0; 45 }
這里有一個HDU3183的例題大家可以移步看一下具體的使用
https://www.cnblogs.com/qq965921539/p/9609015.html