// ST:segement tree; A: target number array voidst_create(vi &ST, const vi &A){ int len = 2<<(sizeof(unsignedint) - __buildin_clz((unsignedint)A.size())); ST.assign(len, 0); // 線段樹歸零 st_build(ST, A, 1, 0, (int) A.size()-1); } voidst_build(vi &ST, const vi &A, int vetex, int L, int R){ if (L==R) ST[vertex] = L; else { int nL = vertex << 1; int nR = (vertex << 1) + 1;
st_build(ST, A, nL, L , ((L+R)>>1)); st_build(ST, A, nR, ((L+R)>>1)+1, R ); int lContent=ST[nL], rContent=ST[nR]; int lValue=A[lContent], rValue=A[rContent]; ST[vertex]=(lValue <= rValue)? lContent : rContent; } }
時間複雜度
\(\Theta(\log n)\)
更新
與建立線段樹的方法相同,將位於數組 i 的數字更新後,即從此葉節點向上執行更新至根節點。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// 以數值 v 更新 A[p] // x: 數根 // L: 數組左端 // R: 數組右端 intupdate(vi &ST, vi &A, int x, int L, int R, int p, int v){ int mid=L+(R-L)/2;
if (L == R) A[x] = v; else { if (p <= mid) update(ST, A, x*2 , L , mid, p, v); // 更新左子樹 else update(ST, A, x*2+1, mid+1, R , p, v); // 更新右子樹 }
intquery(vi &ST, const vi &A, int x, int L, int R, int ql, int qr){ int mid = (L+R)/2, ans = -1; // 當前節點完全位於欲求取之區間之內 if (L>=ql && R<=qr) // 取出該區間值最小的位址 return ST[x]; // 當前節點部分或無位於欲求取之區間之內 if (ql<=mid) { // 取出左區間 ans = query(ST, A, x*2 , L , mid, ql, qr); } if (qr>mid) { // 取出右區間以及比較 int tmp = query(ST, A, x*2+1, mid+1, R , ql, qr); if (ans == -1) ans = tmp; else ans = (A[ans] < A[tmp])? ans:tmp; } return ans; }
intminimum(int left, int right){ // interval [left, right) int ret = std::numeric_limits<int>::max(); left += n; right += n;
while (left < right) { if (left & 1) ret = std::min(ret, data[left++]); if (right & 1) ret = std::min(ret, data[--right]); left >>= 1; right >>= 1; } return ret; }
private: int n; std::vector<int> data; };
intmain(){ SegmentTree st(5); st.update(0, 5); st.update(1, 2); st.update(2, 3); st.update(3, 1); st.update(4, 4); for (int i = 0; i < 5; i++) { std::cout << i << ": " << st.minimum(i, i+1) << std::endl; }
constint N = 1000000; //No. of elements constint logN = ceil(log(N)); //const int logN = sizeof(unsigned int) - __builtin_clz((unsigned int)dist) - 1;;
int value[N]; int cnt[logN][N]; //cnt[i][j]: RMQ index voidconstruct_ST(){ //Initialization for (int i=0; i<N; ++i) cnt[0][i] = i;
// 2^i - 1 < N <=> i < ceil(log(N)) for (int i=1; (1<<i)-1 < N; i++) // j + (2^i-1) < N ; buttom-up 建立 for (int j=0; j+(1<<i)-1 < N; j++) { int L = cnt[i-1][j]; int R = cnt[i-1][j+(1<<(i-1))]; cnt[i][j] = (value[L] <= value[R])? L : R; } }
時間複雜度
\(\Theta(N\log N)\)
空間複雜度
\(\Theta(N \log N)\)
查詢
從表格中找到寬度略短於(相等於)查詢區間的區間,以靠左、靠右的兩條等寬區間,求得查詢區間的最小值:
STable_3
如何知道要查「Range」大小為何的表?
令 \(k\) 為我們所求 → \(k = \lfloor \lg N \rfloor\)
1 2 3 4 5 6 7 8 9
intquery(int a, int b){ int dist = abs(b - a) + 1; // 2^i < N <=> i < cel(logN) ; 區間計算 int i = sizeof(unsignedint) - __builtin_clz((unsignedint)dist) - 1;
int L = cnt[i][a]; // 左區間 int R = cnt[i][b-(1<<i)+1]; // 右區間 return value[L] <= value[R]? L : R; }