ST表的原理及其實現


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM