線段樹與樹狀陣列學習總結 線段樹

2021-08-21 04:43:56 字數 3233 閱讀 2350

點動成線

那麼就是說一條線段可以分成若干個點,再想想我們最常用的一維陣列,構成陣列的是乙個個的變數,如果把變數看成乙個個點,那麼陣列就是一條線了!

而線段樹,就是一棵由線段構成的二叉樹,每個結點都代表一條線段 [a,b](也就是我們前面說的一串變數)。非葉子的結點所對應的線段都有兩個子結點,左兒子代表的線段為 [a,(a+b)/2],右兒子代表的線段為 [((a+b)/2)+1,b]。使用線段樹這一資料結構,可以查詢乙個連續區間中節點的資訊,也可以修改乙個連續區間中結點的資訊。

那麼為什麼不用普通的陣列而是把陣列上又加了」坨」樹呢?

想想我們當是為什麼要學習鍊錶這個資料結構,因為陣列對於元素的新增與刪除效率太低!而線段樹也是為了解決這個問題的所以線段樹的作用是:

優化區間操作的複雜度。

如圖這就是乙個線段樹(建議自己根據定義自己畫一下)

這一小節我們先看單點更新

顧名思義,單點更新就是只更新乙個葉子節點啦

線段樹這一部分函式的引數比較多注意區分!

我們先看兩個基礎的操作

1.修改

就是將資料改了唄,可以使加減或者是覆蓋,這個的話,具體實現的時候稍微改一下即可!

上**(時間複雜度:o(logn)可以從這裡看出其高效性)

void up(int p)

void modify(int p,int l,int r,int

x,int v)

int mid=(l+r)/2;

if(x

<=mid)else

up(p);

}

說明:

1.網上有兩種**,一種沒有up…但是大多數人的都有,於是我就用了有up的了。

2.up函式作用:把兒子結點的資訊更新到父親結點

3.molify函式:這個就是修改函式啦,說一下變數

–1.p:表示當前位置的節點編號,例如我們在一開始是要在全域性進行修改,於是就把p定為根節點。隨著修改的進行,我們要修改p來到達其他節點,例如從a節點到a的右孩子就是:p=p*2+1,就像我們平時用的cur一樣。

–2.l和r:l和r表示的是當前鎖定的區間的範圍,例如剛開始l=1,r=n,就是說範圍在[1,n]上(也就是全域性),但是我發現目標在根的左孩子上,那麼我就要更改l,r為l=l,r=(l+r)/2了。

–3.x:就是要更改的節點編號

–4.v:要更改的值

注意:一定要自己走一遍!!!

2.查詢:

就是查詢區間內節點的值

int query(int p,int l,int r,int x,int y)

intmid=(l+r)/2,res=0;

if(x<=mid)

if(y>mid)

return res;

}

說明:

–1.p和上面一樣

–2.l,r和上面一樣是當前鎖定的節點範圍,一定要注意!!!!

–3.x,y是我要查詢的區間,這個在就是呼叫函式的時候說明的區間,不變!!!,注意區別與l,r的關係!!!

3.建樹

為什麼最後說這個?

因為我學的時候這就不是乙個模組

只需要注意建的樹s的陣列必須開四倍空間!!!

至此所有的單點更新內容全部結束!

給出模板**:

#include 

using

namespace

std;

const

int max_n=10010;

int s[4*max_n];

void up(int p)

void modify(int p,int l,int r,int x,int v)

int mid=(l+r)/2;

if(x<=mid)else

up(p);

}int query(int p,int l,int r,int x,int y)

int mid=(l+r)/2,res=0;

if(x<=mid)

if(y>mid)

return res;

}int main()

int q;

cin>>q;

while(q--)else

還是顧名思義,線段樹的區間更新就是把段的資料統一更新了。

當然我們可以反覆執行單點更新的molify實現,但是效率呢?

線段樹的特點就是犧牲空間換時間,例如:

我要反覆執行[1,1000]區間賦值為1,再賦值為2,再賦值為1,再賦值為2…往復迴圈,那還不如memset高效呢!

所以我們不能像單點更新一樣言聽計從了,有時候也要皮一下,於是我們有了lazy標記,什麼意思呢,你讓我把[1,1000]更新為1,我說好的,然後打乙個標記到[1,1000]的子樹的根節點上寫上改為1000,但是我沒有把根節點的子節點的資料改了,除非你來訪問了,你訪問多少,我打多少,你不訪問,程式結束的時候我可能都沒去真正的賦值!

同樣類似的標記命名還有col等,以下**為col的。這裡直接上模板**了,函式名與單點的一樣,引數略有不同,自己想。

#include 

using

namespace

std;

const

int max_n=10010;

int s[4*max_n],col[4*max_n];

void up(int p)

void down(int p,int l,int r)

}void modify(int p, int l, int r, int x, int y, int c)

down(p, l, r);

int mid = (l + r) / 2;

if (x <= mid)

if (y > mid)

up(p);

}int query(int p, int l, int r, int x, int y)

down(p, l, r);

int mid = (l + r) / 2, res = 0;

if (x <= mid)

if (y > mid)

return res;

}int main()

int q;

cin>>q;

while (q--) else

}return

0;}

線段樹 樹狀陣列 總結

前言在對這三個資料結構進行了粗淺的學習之後,博主發現資料結構的世界是多麼的美妙。然後在博主的專業作死技能加持之下,三個資料結構的大戰一觸即發 對於某些題目 如,單點更新並且查詢區間和 那麼這個時候,我們會發現用樹狀陣列也十分吃香,畢竟這正是樹狀陣列所擅長的!它的時間複雜度均為o log n 相比於線...

樹狀陣列與線段樹

推一下關於樹狀陣列的講解部落格 和線段樹的講解 package test2 public class 線段樹 int len a.length segtree t buildtree 0,len 1,a int sum0 2 query t,0,2 int sum1 3 query t,1,3 查詢...

樹狀陣列與線段樹(三)

找規律題 1.螺旋折線 如下圖所示的螺旋折線經過平面上所有整點恰好一次。對於整點 x,y 我們定義它到原點的距離 dis x,y 是從原點到 x,y 的螺旋折線段的長度。例如 dis 0,1 3,dis 2,1 9 給出整點座標 x,y 你能計算出 dis x,y 嗎?輸入格式 包含兩個整數 x,y...