線段樹入門總結

樹結構的基本思想是分割,普通二叉搜索樹是按照數據來劃分(想了解二叉搜索樹的請移步:Here),線段樹處理的對象是線段(區間也可以看成線段,L==R時爲一個點),它把線段組織成有利於檢索和統計的形式,它的本質是線段的二叉搜索樹。但是它的線段可以分解和合並,線段樹又有一些一般二叉檢索樹沒有的特殊操作。另外線段樹操作的是整個區間,它的時間複雜度不依賴於數據對象。它將一個區間劃分成一些單元區間,每個單元區間對應線段樹中的一個葉結點。使用線段樹可以快速的查找某一個節點在若干條線段中出現的次數,時間複雜度爲O(logN)。而未優化的空間複雜度爲2N,因此有時需要離散化讓空間壓縮。
線段樹的定義:
線段樹是一棵二叉檢索樹,最終將一個區間 [1,n] 劃分爲一些 [ i,i+1 ]的但願區間,每個單元區間對應一個葉節點。葉節點區間只有一個數(L == R)。對於每個線段樹的非葉子節點 [ a, b ],其左兒子 爲 [a,(a+b)/2],右兒子爲 [ (a+b)/2 + 1,b]。所以線段樹是一顆平衡二叉樹。最後節點數目爲 N 即整段區間的長度,其節點數爲 n + n/2 + n/4 + … = 2n。所以其空間複雜度爲 O(2n)。
時間複雜度爲 log n(插入或者讀取均爲log n)。其結構如圖
線段樹的處理對象:
 線段樹的處理對象是一段區間,區間上的格點對應有限個區域的變量,處理問題的時候,抽出區間上的格點,也是明確每個格點對應變量的含義。
線段樹的基本結構:
一棵線段樹,應該具有 插入 , 修改 , 查詢 三個基本功能。
【代碼實現】:
我們以區間最值問題(RMQ)爲例:
我們先聲明線段樹所需的結構體
[cpp] view plain copy

#include <stdio.h>  
#define MAXN 1<<19  
typedef struct  
{  
    int value;      //區間最值  
    int left,right; //區間範圍  
}Tree;  
Tree node[2*MAXN];  
int father[MAXN];   //記錄葉子對應結構體的 下標</span>  

如圖所示:
如果 二叉樹 的 父節點 爲 k,那麼其 左兒子爲 2k,右兒子 爲 2k+1。
現在我們可以建立線段樹裏。
[cpp] view plain copy

//線段樹的建立  
void build(int i, int left, int right){    //i爲結構體數組的下標  
    node[i].left = left;          //爲節點成員初始化  
    node[i].right = right;  
    node[i].value = 0;  
    if(left == right){   //當線段樹的節點爲葉子時,結束遞歸  
        father[left] = i;//將葉子在結構體數組的下標記錄,以便更新是可以自下而上  
        return ;  
    }  
    //現在分別建立該節點的左右孩子  
    build(i<<1,left,(left+right)/2);  
    build( (i<<1)+1,1+(left+right)/2,right);  
    return ;  
}  

因爲我們現在是以最值得問題作爲樣例,所以我們更新數據的話就是單點操作,這裏最值我們以最大值爲例
[cpp] view plain copy

//自上往下的更新,n_i 如上圖所意  
void Updata(int n_i){  
    if(n_i == 1) return ;   //找到了根節點,結束遞歸  
    int fa = n_i/2;     //找到了父節點  
    int a = node[2*fa].value;  //該父節點的左兒子的值  
    int b = node[2*fa + 1].value;//該父節點的右兒子的值  
    node[fa].value = a>b?a:b;       //更新節點數據  
    Updata(fa);             //遞歸更新  
    return ;  
}  

現在我們剩下的就差查詢操作了
[cpp] view plain copy

int Max = -MAXN;  
//k爲結構體下標,通常我都從根節點開始查詢,所以,通常我們初始化時爲1  
//查詢區間爲 [ left, right ]  
void Query(int k,int left,int right){  
    //當查詢區間完全重合時  
    if(node[k].left == left && node[k].right == right){  
        Max = Max > node[k].value ? Max : node[k].value;  
        return ;  
    }  
    //對左子樹進行操作  
    if(left <= node[2*k].right){  //如果與左區間有交集  
        if(right <= node[2*k].right)  //如果完全包含於左區間,則查詢範圍不變  
            Query(2*k,left,right);  
        else//否則這將區間查分開,先查詢左邊的  
            Query(2*k,left,node[2*k].right);  
    }  
    //對右子樹進行操作  
    if(right >= node[2*k+1].left){  //如果與右區間有交集  
        if(left >= node[2*k+1].left)  //如果完全包含於右區間,則查詢範圍不變  
            Query(2*k+1,left,right);  
        else//否則這將區間查分開,先查詢右邊的  
            Query(2*k+1,node[2*k+1].left,right);  
    }  
    return ;  
}  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章