資料結構 線段樹

2022-05-16 10:44:57 字數 1725 閱讀 1682

線段樹,類似區間樹,它在各個節點儲存一條線段(陣列中的一段子陣列),主要用於高效解決連續區間的動態查詢問題,由於二叉結構的特性,它基本能保持每個操作的複雜度為\(o(logn)\)。

線段樹的每個節點表示乙個區間,子節點則分別表示父節點的左右半區間,例如父親的區間是\([a,b]\),那麼\((c=(a+b)/2)\)左兒子的區間是\([a,c]\),右兒子的區間是\([c+1,b]\)。

下面我們從乙個經典的例子來了解線段樹,問題描述如下:從陣列a[0...n-1]中查詢某個陣列某個區間內的最小值,其中陣列大小固定,但是陣列中的元素的值可以隨時更新。

對這個問題乙個簡單的解法是:遍歷陣列區間找到最小值,時間複雜度是\(o(n)\),額外的空間複雜度\(o(1)\)。當資料量特別大,而查詢操作很頻繁的時候,耗時可能會不滿足需求。

另一種解法:使用乙個二維陣列來儲存提前計算好的區間\([i,j]\)內的最小值,那麼預處理時間為\(o(n^2)\),查詢耗時\(o(1)\), 但是需要額外的\(o(n^2)\)空間,當資料量很大時,這個空間消耗是龐大的,而且當改變了陣列中的某乙個值時,更新二維陣列中的最小值也很麻煩。

我們可以用線段樹來解決這個問題:預處理耗時\(o(n)\),查詢、更新操作\(o(logn)\),需要額外的空間\(o(n)\)。根據這個問題我們構造如下的二叉樹

葉子節點是原始組數\(a\)中的元素

非葉子節點代表它的所有子孫葉子節點所在區間的最小值

例如對於陣列\([2, 5, 1, 4, 9, 3]\)可以構造如下的二叉樹(背景為白色表示葉子節點,非葉子節點的值是其對應陣列區間內的最小值,例如根節點表示陣列區間\(a[0...5]\)內的最小值是1):

由於線段樹的父節點區間是平均分割到左右子樹,因此線段樹是完全二叉樹,對於包含\(n\)個葉子節點的完全二叉樹,它一定有\(n-1\)個非葉節點,總共\(2n-1\)個節點,因此儲存線段是需要的空間複雜度是\(o(n)\)。那麼線段樹的操作:建立線段樹、查詢、節點更新 是如何運作的呢(以下所有**都是針對求區間最小值問題)?

對於線段樹我們可以選擇和普通二叉樹一樣的鏈式結構。由於線段樹是完全二叉樹,我們也可以用陣列來儲存,下面的討論及**都是陣列來儲存線段樹,節點結構如下(注意到用陣列儲存時,有效空間為\(2n-1\),實際空間確不止這麼多,比如上面的線段樹中葉子節點\(1\)、\(3\)雖然沒有左右子樹,但是的確占用了陣列空間,實際空間是滿二叉樹的節點數目。

建線段樹的過程

void build(int l, int r, int root)

int mid=(l+r)>>1;

build(l,mid,root<<1);

build(mid+1,r,root<<1|1);

pushup(root);

}

pushup過程
void push(int root)

查詢線段樹
int query(int ansl, int ansr, int l, int r,int root)

單節點更新
void update(int pos, int c, int l, int r, int root)

int mid=(l+r)/2;

if (pos<=mid) update(pos,c,l,mid,root<<1); else update(pos,c,mid+1,r,root<<1|1);

pushup(root);

}

資料結構 線段樹

啦啦啦啦啦啦線段樹是個好東西 好吧並沒有什麼好的 但貌似還是很好啊 線段樹就是一棵樹!顧名思義 又是這個詞 就是求關於一段的某些什麼什麼東西。比如區間最大值啊什麼的。引用百科知識 線段樹是一種二叉搜尋樹,與區間樹相似,它將乙個區間劃分成一些單元區間,每個單元區間對應線段樹中的乙個葉結點。對於線段樹中...

資料結構 線段樹

一 目標 1.如何快速的查詢出下列陣列arr 2,5 的和 2。以及更新arr 4 為6。用普通的方法查詢的複雜度為o n 更新的複雜度為o 1 這時候我們可以用線段樹來快速完成這些操作,複雜度為logn。二 內容 如何建立,查詢,更新線段樹。public class qurqpd int tree...

資料結構 線段樹

線段樹是一顆平衡的二叉搜尋樹,他以空間換區時間,讓線性查詢加速log級別的查詢,用到的演算法主要是二分搜尋和遞迴。例如 有陣列data 我有乙個需求,我需要頻繁的查詢區間i j的sum和。這裡先給出兩個解決方案 如果使用最普通的演算法遍歷,那麼查詢和更新的複雜度為o n 當然你還可以使用動態規劃,定...