線段樹 入門詳解
概念(copy度娘):
線段樹是一種二叉搜索樹,與區間樹相似,它將一個區間劃分成一些單元區間,每個單元區間對應線段樹中的一個葉結點。
使用線段樹可以快速的查找某一個節點在若幹條線段中出現的次數,時間復雜度為O(logN)。而未優化的空間復雜度為2N,因此有時需要離散化讓空間壓縮。
通俗地講:
線段樹就是把一個線段轉變為一個二叉樹,如下所示:
一個線段長度為4,則把它變成線段樹,就是這個樣子
這樣如果你想改變一個區間的值,只用O(logn)。比一般算法快了許多。
但是空間復雜度就會高一些,比如本來這個線段只占4,現在變成了7.
最基本的題目:
為了方便大家了解線段樹的建樹+改變+查找,我就以最基本的修改區間範圍值的題做示範:
題曰:給定一個長度為L的數組,每一位的開始值都是0,然後給出n條信息,每行信息包括a,b,c。
含義是在開區間(a,b)中所有的值加上c
然後輸入一個k,詢問k的值。
輸入:
第一行輸入信息數n,數組長度L。
接下來2到n+1行輸入a,b,c。
最後一行輸入k
輸出:
k的值
輸入示例:
2 4
1 3 1
2 4 4
2
輸出示例:
5
建樹:
看完題後我們開始建樹。
這是線段樹中最簡單的部分。
順便說一下,線段樹英文是:segtree
struct node { int l,r,data; }segtree[100010];//首先你要建一個線段樹,l是這個點的左邊(就比如上圖根節點的1),r是這個點的右邊,比如上圖根節點的4。data就是這個點的增減標記,記錄這個店增加了幾void build(int index,int left,int right)//遞歸實現建樹 { if(left==right)//如果這個點是葉節點,就return; { segtree[index].r=right; segtree[index].l=left; return ; } segtree[index].l=left;//給此節點的左右邊界賦值 segtree[index].r=right; int mid=(left+right)/2; build(index*2,left,mid);//遞歸建立左子樹和右子樹 build(index*2+1,mid+1,right); }
一上就是簡單的建樹,用到了遞歸,大家應該都能看懂。
增減值:
我實在不會術語,就這樣吧。下面來教大家怎樣改變線段樹的值。
想要改變線段樹的值,不是直接改變,因為線段樹上只有一個l一個r沒有表示實際的值。
這時候我們就要創建一個data,來標記這個線段樹的增減。
比如我們要使2~4區間加1,可以這樣
從根節點1~4區間開始搜索,發現它的左右子節點(1~2、3~4)都有2~4區間,所以搜索兩邊。
先搜索左子節點,發下這個節點的左節點(1~1)沒有2~4,於是不搜索,接著搜索右節點(2~2),發現此節點被完全包括在了2~4,因此義不容辭地將這個點標記從原本的0加上一個1,並且斷絕此節點搜索。
我們回到最開始的右節點,(3~4),發現這個節點又被完全包括在2~4裏面於是標記從零到一,然後結束搜索。
一番搜索過後,線段樹,也就是segtree[].data標記如下:
之後用這個來如何處理,我稍後會講
現呈上代碼(編譯器壞了,手打的,難免會有一些bug,見諒)
void update(int index,int left,int right,int c)//還是遞歸標記 { if(segtree[index].l>=left && segtree[index].r<=right)//如果全都被包括,就直接加上去 { segtree[index].data+=c; return ; } int mid=(segtree[index].l+segtree[index].r)/2; if(mid>=left)//分別判斷是否包含一部分 update(index*2,left,right,c); if(mid<right) update(index*2+1,left,right,c); }
查詢:
最激動人心的時刻到了,查詢!!!其實很簡單,就是從根節點一直走到你要查詢的葉子節點,一路上都加上segtree[].data就好了。
上面的圖,你查詢3,根節點+0,往右邊走+1,再往左邊走+0,於是結果就是1.
代碼如下:
void search(int index,int k,int ans) { int left=segtree[index].l; int right=segtree[inrdex].r; if(l==k && r==k) { cout<<ans; return ; } int mid=(left+right)/2; if(k<=mid) search(index*2,k,ans+segtree[index].data); if(k>mid) search(index*2+1,k,ans+segtree[index].data); }
好了,不知道完整代碼還要不要,應該不要了吧。
開始build(1,1,L);
就是沒讀入一條信息,a,b,c然後調用update(1,a,b,c);
最後輸入k調用search(1,k,0)
口胡完畢,沒聽懂評論就好。
線段樹 入門詳解