1. 程式人生 > >線段樹入門

線段樹入門

擴大 中一 復雜度 open else for 個數 hide line

一。概念

線段樹是用於處理區間的復雜度為O(log n)一類數據結構。線段樹是一棵完美二叉樹(區別於完全二叉樹)。樹上的每個節點維護一個區間,且為父親節點的區間二等分後的其中一個子區間。

技術分享

二. 基於線段樹的RMQ操作(根據維護的信息不同,線段樹還可以實現其他功能)

  1. 給定s和t,求a[s]~a[t]的最值
  2. 給定i和x,將a[i]的值修改為x

三. 基於線段樹的查詢

  例如查詢區間的最小值

  即使查詢的是一個比較大的區間,由於較靠上的節點對應較大的區間,通過這些區間就可以知道大部分值中的最小值,從而可以訪問較少的節點來求得最小值

  1. 如果所查詢的區間和當前區間完全沒有交集,那麽久返回一個不影響答案
    的值(如求解最小值是返回INF)
  2. 如果所查詢的區間完全包含了當前節點所對應的區間,那麽久返回當前節點的值(例如求解區間[1,7]時,查詢到的[1,4]區間的值可以直接返回)
  3. 以上兩種都不符合,就對兩個兒子遞歸處理,返回兩個結果中的較小者

技術分享
 1 int n, dat[2 * maxn - 1];
 2 
 3 void init(int n_) {
 4     // 為了簡單起見,把元素個數擴大到2的冪次
 5     n = 1;
 6     while (n < n_) {
 7         n *= 2;
 8     }
 9     for (int i = 0; i < 2
* n - 1; i++) { 10 dat[i] = INF; 11 } 12 } 13 14 // 把第k個值(0 ~ indexed)更新為a 15 void update(int k, int a) { 16 // 葉子節點 17 k += n - 1; 18 dat[k] = a; 19 // 向上更新 20 while (k > 0) { 21 k = (k - 1) / 2; 22 dat[k] = min(dat[k * 2 + 1], dat[k * 2 + 2]); 23 } 24 }
25 26 // 求[a, b]的最小值 27 // 後面的參數時為了方便計算而傳入的 28 // k是節點的編號,l, r表示這個節點對應的是[l, r) 左開右閉 29 // 在外部調用時, 用(a, b, k, l, r) 30 31 int query(int a, int b, int k, int l, int r) { 32 //如果[a, b]和[l, r]不相交,則返回一個特殊值 33 if (r <= a || b <= l) return INF; 34 35 //如果[a, b)完全包含[l, r), 則返回當前節點的值 36 if (a <= l && r <= b) return dat[k]; 37 else { 38 int vl = query(a, b, k * 2 + 1, l, (l + r) / 2); 39 int vr = query(a, b, k * 2 + 2, (l + r) / 2, r); 40 return min(vl, vr); 41 } 42 }
View Code

線段樹入門