線段樹的建立,區間查詢,單點更新程式碼展示(以求和為例)
阿新 • • 發佈:2018-12-23
線段樹這個東西真的夠奇怪的。。
線段數的節點表示的是一段區間的值,它要求區間之間必須滿足能夠相加的條件,不然不能用線段樹表示。
線段樹包括4中操作,包括建立,區間查詢,單點更新和區間更新,而區間更新是一個最難的部分,需要用到標記。。
這裡就說一下前三種相對簡單的操作。
在這之前,補充兩個知識點:
x>> 1 表示的是x/2;
x<<1 表示的是x*2;
|運算: 與偶數進行運算時必在基礎上+1,與奇數運算結果不變
建立:
建立的時候是從一開始的區間開始劃分,通過二分的思想,將一個大區間分成兩個小區間。當左區間等於右區間時,就可已將他看成單個點進行處理了。然後進行回溯,從下往上求出各個節點的值。
程式碼如下:
//建樹
void build (int l,int r,int re) //[l,r]表示節點表示的區間,re表示節點標號
{
if(l==r)
{
tree[re]=a[l];
return ;
}
int mid=(l+r)>>1;
build (l,mid,re<<1); //將區間二分
build (mid+1,r,re<<1|1);
//回溯求出根節點的值
Pushup (re);
}
上面有個Pushup,這個函式是給根節點賦值的
它的程式碼如下:
//更新根節點的值
void Pushup (int re) //求出根節點的值
{
tree[re]=tree[re<<1]+tree[re<<1|1];
}
下一個就是單點更新了。
單點更新相當於就是對單個左右區間相等的節點的值進行修改 ,這實際上就是從上往下找要修改的單點,修改完之後進行回溯,
依次往上更新根節點的值。
程式碼如下:
//單點更新 void add (int loc,int data,int l,int r,int re) //loc表示更新的位置,data表示加上的值 { //表示找到這個單點了 if(l==r) { tree[re]+=data; } int mid=(l+r)>>1; //如果小於中間值,那麼肯定在左子樹那裡,不小於的話就去右子樹找了 if(loc<=mid) add (loc,data,l,mid,re<<1); else add (loc,data,mid+1,r,re<<1|1); //別忘記也要更新根節點的值哦 Pushup(re); }
區間查詢:
這一部分就有點難了。
區間查詢的意思就是給定一個區間查詢這個區間的資訊。
讓我們想想, 如果節點所表示的區間完全包含在所要查詢的區間的話,那麼我們可以直接返回它的值就可以了。
如果超出了查詢範圍了的話, 我們就要進行取捨了。
( 1)如果節點的中點mid大於所要查詢的左區間left的話, 那麼必定該區間中點左邊必定有一部分是在查詢區間, 這樣我們就可以查詢節點的左子樹了。千萬不能也查詢右子樹,因為mid與right的相對大小不一定, 如果mid>right那麼就返回錯誤值了。 。
( 2)如果mid<right的話, 與上面同理,查詢右子樹。
程式碼如下:
//區間查詢
int que (int left,int right,int l,int r,int re) //[left,right]表示要查詢的區間
{
if(l>=left&&r<=right) //說明此時節點所代表的區間都在查詢區間之內,可以直接返回了
return tree[re];
int mid=(l+r)>>1;
//通過ans記錄區間之和.
int ans=0;
//走到這裡,說明區間超出查詢範圍了,需要進行篩選
//如果區間終點大於等於left,說明該區間左邊的需要納入查詢範圍
if(mid>=left)
{
ans+=que (left,right,l,mid,re<<1);
}
//如果區間終點小於right,說明該區間的右邊需要納入查詢範圍
if(mid<right)
{
ans+=que (left,right,mid+1,r,re<<1|1);
}
return ans;
}