线段树是一种能够实现快速查询区间,区间修改,单点修改的数据结构。其用途是在查询一个指定区间内的信息,并可在同样的时间复杂度支持更新等操作。
线段树是一个平衡的二元树,所有叶子到根的距离最多只差1。令整个区间的长度为N,则其有N个叶节点,每个叶节点代表一个单位区间,每个内部结点代表的区间为其两个儿子代表区间的联集。
线段树的一般结构如图所示:(图是借鉴别人的,侵删)
可以看出根节点维护整个树的区间,叶子节点维护一个点的区间。
要想利用线段树,首先要建立一颗线段树:
Code:
void bulid_tree(int l,int r,int n)
{
sum[n].l=l;
sum[n].r=r;
sum[n].maxnd=0;
sum[n].minnd=maxn;
if(l==r)
{
sum[n].maxnd=sum[n].minnd=arr[l];
return ;
}
int mid=(l+r)>>1;
bulid_tree(l,mid,n<<1);
bulid_tree(mid+1,r,n<<1|1);
sum[n].maxnd=max(sum[n<<1].maxnd,sum[n<<1|1].maxnd);
sum[n].minnd=min(sum[n<<1].minnd,sum[n<<1|1].minnd);
}
说明:这是一poj3264题为背景的代码
前四题就是讲树的区间赋值,当l==r的时候,就到了叶子节点,就可以对叶子节点赋值了(这是递归建树的终止条件),然后在建立左子树,在建立右子树,然后保存这个区间的最大最小值。
实现了树,接下来就要实现查询的功能:
Code:
void query(int l,int r,int n)
{
if(sum[n].l==l&&sum[n].r==r)
{
ansmax=max(ansmax,sum[n].maxnd);
ansmin=min(ansmin,sum[n].minnd);
return ;
}
int mid=(sum[n].l+sum[n].r)>>1;
if(r<=mid)
{
query(l,r,n<<1);
}
else if(l>mid)
{
query(l,r,n<<1|1);
}
else
{
query(l,mid,n<<1);
query(mid+1,r,n<<1|1);
}
}
递归的终止条件是需要查询的区间正好就是根节点维护的区间,那么就返回要查询的结果
如果需要查询的区间右端点比这个节点的中间值来的小,那就从这个节点的左孩子开始找,要是l比中间值来的大,
那么就从右孩子开始找,否则就先找到左孩子,再找到右孩子。
这个代码:
Code:
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000010;
struct tree{
int maxnd,minnd;
int r,l;
};
int ansmin,ansmax;
int n,arr[maxn];
int Q,N;
tree sum[maxn<<2];
void bulid_tree(int l,int r,int n)
{
sum[n].l=l;
sum[n].r=r;
sum[n].maxnd=0;
sum[n].minnd=maxn;
if(l==r)
{
sum[n].maxnd=sum[n].minnd=arr[l];
return ;
}
int mid=(l+r)>>1;
bulid_tree(l,mid,n<<1);
bulid_tree(mid+1,r,n<<1|1);
sum[n].maxnd=max(sum[n<<1].maxnd,sum[n<<1|1].maxnd);
sum[n].minnd=min(sum[n<<1].minnd,sum[n<<1|1].minnd);
}
void query(int l,int r,int n)
{
if(sum[n].l==l&&sum[n].r==r)
{
ansmax=max(ansmax,sum[n].maxnd);
ansmin=min(ansmin,sum[n].minnd);
return ;
}
int mid=(sum[n].l+sum[n].r)>>1;
if(r<=mid)
{
query(l,r,n<<1);
}
else if(l>mid)
{
query(l,r,n<<1|1);
}
else
{
query(l,mid,n<<1);
query(mid+1,r,n<<1|1);
}
}
int main()
{
while(cin>>N>>Q)
{
for(int i=1;i<=N;i++)
{
cin>>arr[i];
}
bulid_tree(1,N,1);
int a,b;
for(int j=1;j<=Q;j++)
{
cin>>a>>b;
ansmin=maxn;
ansmax=0;
query(a,b,1);
cout<<ansmax-ansmin<<endl;
}
}
return 0;
}
另外在维基百科上找到了一种线段树的模板(注释是自己加上的)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
namespace SegTree {
#define maxn 1000000
class SegmentTree {
#define lson (o<<1)
#define rson (o<<1|1)
#define mid ((l+r)>>1)
public :
int addv[maxn], maxv[maxn], minv[maxn], sumv[maxn];
int arr[maxn];
int N;
private:int _max(const int& _, const int& __) { return _>__?_:__; }
private:int _min(const int& _, const int& __) { return _<__?_:__; }
public : int pushup(int o) {
minv[o] = _min(minv[lson], minv[rson]);//求最小值
maxv[o] = _max(maxv[lson], maxv[rson]);//求最大值
sumv[o] = sumv[lson] + sumv[rson];//求区间的和
return 0;
}
public : int pushdown(int o, int l, int r) {//扎实利用lazy标记,向下延伸
if(addv[o] == 0) return -1;
addv[lson] += addv[o]; addv[rson] += addv[o];
minv[lson] += addv[o]; minv[rson] += addv[o];
maxv[lson] += addv[o]; maxv[rson] += addv[o];
sumv[lson] += addv[o] * (mid-l+1); sumv[rson] += addv[o] * (r-mid);
addv[o] = 0;
}
public : int Build(int o, int l, int r) {//建树,没啥好说的
addv[o] = 0;
if(l == r) {
maxv[o] = arr[l];minv[o] = arr[l];sumv[o] = arr[l];
return 0;
}
Build(lson, l, mid);
Build(rson, mid+1, r);
pushup(o);
return 0;
}
public : int optadd(int o, int l, int r, int ql, int qr, int addval) {
if(ql > r or qr < l) return 0;//对单点修改
if(ql <= l and r <= qr) {
addv[o] += addval;
sumv[o] += addval * (r-l+1);
return 0;
}
pushdown(o, l, r);//向下传递变化
optadd(lson, l, mid, ql, qr, addval);//左孩子也要改
optadd(rson, mid+1, r, ql, qr, addval);//右孩子也要改
pushup(o);
}
public : int query_sum(int o, int l, int r, int ql, int qr) {//查询区间和
if(ql > r or qr < l) return 0;
if(ql <= l and r <= qr) {
return sumv[o];
}
pushdown(o, l, r);
return query_sum(lson, l, mid, ql, qr) + query_sum(rson, mid+1, r, ql, qr);
}
public : int query_min(int o, int l, int r, int ql, int qr) {
if(ql > r or qr < l) return 0;//查询区间最小值
if(ql <= l and r <= qr) {
return minv[o];
}
pushdown(o, l, r);
return _min(query_min(lson, l, mid, ql, qr), query_min(rson, mid+1, r, ql, qr));
}
public : int query_max(int o, int l, int r, int ql, int qr) {
if(ql > r or qr < l) return 0;//查询区间最大值
if(ql <= l and r <= qr) {
return maxv[o];
}
pushdown(o, l, r);
return _max(query_max(lson, l, mid, ql, qr), query_max(rson, mid+1, r, ql, qr));
}
};
}
//End of SegmentTree
using namespace SegTree;