1. 程式人生 > >線段樹 入門詳解

線段樹 入門詳解

ear 接下來 數組 編譯器 一位 離散化 都是 並且 建立

概念(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)

口胡完畢,沒聽懂評論就好。

線段樹 入門詳解