線段樹是一種二叉搜尋樹,與區間樹相似,它將乙個區間劃分成一些單元區間,每個單元區間對應線段樹中的乙個葉結點。
對於線段樹中的每乙個非葉子節點[a,b],它的左兒子表示的區間為[a,(a+b)/2],右兒子表示的區間為[(a+b)/2+1,b]。因此線段樹是平衡二叉樹,最後的子節點數目為n,即整個線段區間的長度。
使用線段樹可以快速的查詢某乙個節點在若干條線段中出現的次數,時間複雜度為o(logn)。而未優化的空間複雜度為2n,因此有時需要離散化讓空間壓縮。
【以下以 求區間最大值為例】
先看宣告:
#include #include const int maxnode = 2097152;
const int max = 1000003;
struct nodenode[maxnode];
int father[max]; // 每個點(當區間長度為0時,對應乙個點)對應的結構體陣列下標
【建立線段樹(初始化)】:
由於線段樹是用二叉樹結構儲存的,而且是近乎完全二叉樹的,所以在這裡我使用了陣列來代替鍊錶上圖中區間上面的紅色數字表示了結構體陣列中對應的下標。
在完全二叉樹中假如乙個結點的序號(陣列下標)為 i ,那麼 (二叉樹基本關係)
i 的父親為 i/2,
i 的另乙個兄弟為 i/2*2 或 i/2*2+1
i 的兩個孩子為 i*2 (左) i*2+1(右)
有了這樣的關係之後,我們便能很方便的寫出建立線段樹的**了。
void buildtree(int i,int left,int right)
// 該結點往 左孩子的方向 繼續建立線段樹,線段的劃分是二分思想,如果寫過二分查詢的話這裡很容易接受
// 這裡將 區間[left,right] 一分為二了
buildtree(i<<1, left, (int)floor( (right+left) / 2.0));
// 該結點往 右孩子的方向 繼續建立線段樹
buildtree((i<<1) + 1, (int)floor( (right+left) / 2.0) + 1, right);
}
【單點更新線段樹】:
由於我事先用 father[ ] 陣列儲存過 每單個結點 對應的下標了,因此我只需要知道第幾個點,就能知道這個點在結構體中的位置(即下標)了,這樣的話,根據之前已知的基本關係,就只需要直接一路更新上去即可。
void updatatree(int ri)
【查詢區間最大值】:
將一段區間按照建立的線段樹從上往下一直拆開,直到存在有完全重合的區間停止。對照圖例建立的樹,假如查詢區間為 [2,5]
紅色的區間為完全重合的區間,因為在這個具體問題中我們只需要比較這 三個區間的值 找出 最大值 即可。
int max = -1<<20;
void query(int i,int l,int r)
i = i << 1; // get the left child of the tree node
if (l <= node[i].right)
i += 1; // right child of the tree
if (r >= node[i].left)
}
hdu 1754解題報告
// hdu 1754 i hate it 線段樹 -- 單點更新
// 有效結點: 20w
// 深度達到:(lg20w)/(lg2) +1 .= 18.61 .= 19
// 其完全二叉樹 總結點 個數為: (1<<19) - 1 個
// 區間最大值問題
///*test data
5 61 2 3 4 5
q 1 5
u 3 6
q 3 4
q 4 5
u 2 9
q 1 5
*/#include #include #include using namespace std;
const int maxnode = 524288; // 1<<19
const int maxst = 200001;
struct stust[maxnode];
int father[maxst];
void buildtree(int i,int left,int right)
buildtree(i<<1, left, (int)floor( (right+left) / 2.0));
buildtree((i<<1) + 1, (int)floor( (right+left) / 2.0) + 1, right);
}void updatatree(int ri)
int fi = ri / 2; // 父結點
int a = st[fi<<1].grade; // 該父結點的兩個子結點
int b = st[(fi<<1)+1].grade;
st[fi].grade = (a > b)?(a):(b);
updatatree(ri/2);
}int max;
void query(int i,int l,int r)
i = i << 1; // left child of the tree
if (l <= st[i].right)
i += 1; // right child of the tree
if (r >= st[i].left)
}int main()
while(n_q--)else
} }return 0;
}
線段樹的簡單操作(入門級別)
一些線段數的基本操作 比如建樹,單點修改,單點查詢,單點修改和區間查詢 區間和 區間修改和區間查詢,甚至 單點詢問,區間修改。這些操作都是有著差異性。尤其是區間修改,需要用到llazy標記。另外,建樹也分為很多種 取決於每個父結點的意義 比如以最大或最小值建樹 有的題還讓你求乙個區間內最大與最小值之...
入門級演算法
今天我想記錄的是一些關於入門級別的演算法。將乙個字串s對映為乙個整數,使得該整數可以唯一的代表字串s。先假設字串均由大寫字母a z構成,不妨設a z為0 25,即將26個大寫字母對應到了二十六進製制中。按照將二十六進製制轉換為十進位制的思路,即可實現將字串對映為整數的需求。題目 給出n個字串 恰好由...
mbox Unix 的入門級郵箱
雖然老套卻很好用本章介紹一些基本的郵件 雖然社交網路有很多優點,但是在未來的幾年中,郵件仍將是網際網路的主打產品 分析工具和技術,以回答以下問題 誰傳送的郵件最多?一天中是否存在某個特定的時間 或是一周中的某天 這時候發件人最可能收到對乙個問題的回覆?幾個人之間,誰傳送的訊息最多?討論最熱烈的話題是...