樹秀於林風必摧之——線段樹


關於線段樹,其實我一開始也是很懵的,但看久了也就習慣了。

  以下是我對線段樹的一點理解,寫得不好,也請各位看官見諒。

  搜狗定義:線段樹(Segment Tree)是一種二叉搜索樹,它將一個區間划分成一些單元區間,每個單元區間對應線段樹中的一個葉結點。

  定義還是很顯然的。

  那么線段樹都能做些什么呢?

  在n個數中有m個詢問,詢問如下:

  1.把q這個數改成v  O(logn);

  2.求在1~n這個區間的和.

  接下來我們講原理(當然原理也是我自己的理解,可能不是正解,但我想,線段樹這個東西大概就是這樣的吧)

  首先看圖

  

   下面我們將原理

  1.它是用“二進制”存儲的

  什么是“二進制”存儲?

  眾所周知,用二的倍數可以表示所有的數

  例:13=1+4+8;

  (具體原理請參考二進制和十進制的轉換)

  那么線段樹也是這樣:

  如:我們要查找(2,5)這個區間,那它就是2+(3,4)+5所代表的數(區間和)

  2.它是“二分查找”(當然不是嚴格意義上的)

  二分查找不再贅述

  例:當我們要把6這個位置上所在的數改為★,那我們一定是這樣查找的:

  (1,8)->(5,8)->(5,6)->(6)->(★)

  那么怎么實現呢?我大概總結了一下幾個步驟:

  1.建立線段樹

  2.查找位置+動作

  具體代碼如下

  

int a[maxn];//每個數的值 
int sum[maxn*4];//區間和 


void update(int rt)
{
    sum[rt]=sum[rt*2]+sum[rt*2+1];//前綴和思想 
} 

void build(int l,int r,int rt)//建立樹,左孩子,右孩子,根
{
    if(l==r)
    {
        sum[rt]=a[l];
        return ;//邊界 
    }
    int m=(l+r)/2;
    build(1,m,rt*2);
    build(m+1,r,rt*2+1);
    update(rt);//合並兩個兒子 
} 

 

//如果不理解左孩子右孩子為什么要那樣寫的,看這里:

以根節點為例:(1)

左孩子:1*2=2;

右孩子:1*2+1=3;

這樣就能保證樹在數組里存滿且不重復。

void modify(int l,int r,int rt,int p,int v)//將p的位置上的數改為v 
{
    if(l==r)
    {
        sum[rt]=v;
        return ;//找到這個數了,改值。當然我們也會順便把與它相關的所有值都改掉 
    }
    int m=(l+r)/2;
    if(p<=m)
        modify(1,m,rt*2,p,v);
    else
        modify(m+1,r,rt*2+1,p,v);//二分查找 
    update(rt);
} 

int query(int l.int r,int rt,int nowl,int nowr)//詢問(nowl,nowr)這個區間和 
{
    if(nowl<=1&&r<=nowr)//邊界 
        return sum[rt];
    int m=(l+r)/2;
    int ans=0;
    if(nowl<=m)ans+=query(1,m,rt*2,nowl,nowr);
    if(m<nowr)ans+=query(m+1,r,rt*2+1,nowl,nowr);//查找求和 
    return ans;
}

這是兩次詢問。

主函數的話就依據情況調用這三個函數就好了

那我要講的,大概就是這些了。

byebey!^_^

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM