1:概述
線段樹,類似區間樹,是乙個完全二叉樹,它在各個節點儲存一條線段(陣列中的一段子陣列),主要用於高效解決連續區間的動態查詢問題,由於二叉結構的特性,它基本能保持每個操作的複雜度為o(lgn)!
性質:父親的區間是[a,b],(c=(a+b)/2)左兒子的區間是[a,c],右兒子的區間是[c+1,b],線段樹需要的空間為陣列大小的四倍。
2:基本操作(demo用的是查詢區間最小值)
線段樹的主要操作有:
(1):線段樹的構造 void build(int node, int begin, int end);
主要思想是遞迴構造,如果當前節點記錄的區間只有乙個值,則直接賦值,否則遞迴構造左右子樹,最後回溯的時候給當前節點賦值
#include using namespace std;const int maxind = 256;
int segtree[maxind * 4 + 10];
int array[maxind];
/* 建構函式,得到線段樹 */
void build(int node, int begin, int end)
}
int main()
//build函式建立線段樹
void build(int l,int r,int rt)
int m=(l+r)>>1;
//左右遞迴
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
//更新資訊
pushup(rt);
}
(2)點修改:
假設a[l]+=c:
void update(int l,int c,int l,int r,int rt)int m=(l+r)>>1;
//根據條件判斷往左子樹呼叫還是往右
if(l <= m) update(l,c,l,m,rt<<1);
else update(l,c,m+1,r,rt<<1|1);
pushup(rt);//子節點的資訊更新了,所以本節點也要更新資訊
}
點修改其實可以寫的更簡單,只需要把一路經過的sum都+=c就行了,不過上面的**更加規範,在題目更加複雜的時候,按照格式寫更不容易錯。
(3)區間查詢(本題為求和):
詢問a[l..r]的和
注意到,整個函式的遞迴過程中,l,r是不變的。
首先如果當前區間[l,r]在[l,r]內部,就直接累加答案
如果左子區間與[l,r]有重疊,就遞迴左子樹,右子樹同理。
int query(int l,int r,int l,int r,int rt)int m=(l+r)>>1;
//左子區間:[l,m] 右子區間:[m+1,r] 求和區間:[l,r]
//累加答案
int ans=0;
if(l <= m) ans+=query(l,r,l,m,rt<<1);//左子區間與[l,r]有重疊,遞迴
if(r > m) ans+=query(l,r,m+1,r,rt<<1|1); //右子區間與[l,r]有重疊,遞迴
return ans;
}
**:
線段樹 01 線段樹基礎
物理上 public class segmenttree public int getsize public e get int index 返回完全二叉樹的陣列表示中,乙個索引所表示的元素的左孩子節點的索引 private int leftchild int index 返回完全二叉樹的陣列表示中...
線段樹 基礎
d 基礎 time limit 1000msmemory limit 32768kb64bit io format i64d i64u submit status description c國的死對頭a國這段時間正在進行軍事演習,所以c國間諜頭子derek和他手下tidy又開始忙乎了。a國在海岸線沿...
線段樹基礎
寫得不錯啊。前段時間寫了篇 搞懂樹狀陣列 如果說樹狀陣列是優雅的,那麼線段樹就是萬能的。有句話就叫 樹狀陣列能做的線段樹都能做,但是樹狀陣列能做的堅決用樹狀陣列!因為線段樹本來的內容狠豐富的,主要有單點跟新 區間跟新,最值詢問 區間詢問 反正就是對於區間進行的動態跟新詢問都能較高效的完成。對於初學者...