點動成線
那麼就是說一條線段可以分成若干個點,再想想我們最常用的一維陣列,構成陣列的是乙個個的變數,如果把變數看成乙個個點,那麼陣列就是一條線了!
而線段樹,就是一棵由線段構成的二叉樹,每個結點都代表一條線段 [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...