您的位置:首页 > 理论基础 > 数据结构算法

【数据结构】线段树总结(一)

2016-08-13 11:00 441 查看
线段树在求区间某些值,如区间求和,区间最值等具有明显优势,能将原本朴素的求法O(n)的复杂度减小到 log(n)

HDU 1754 - I Hate It

Online judge : http://acm.hdu.edu.cn/showproblem.php?pid=1754

这是线段树在求区间最大值的一个应用

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

const int MAXN = 200010;

struct seg
{
int l;//表示该区间下标的最小值
int r;//表示该区间下标的最大值
int n;//区间的某个数值,这里指最大值
}T[4*MAXN];

void build(int l,int r,int id)
{
T[id].l=l;
T[id].r=r;
T[id].n=0;

if(l == r)
{
return;
}

int mid = (l+r)/2;

build(l,mid,2*id);//递归求左儿子相关信息
build(mid+1,r,2*id+1);//递归求右儿子相关消息
}

//更新区间值。三个参数分别指要更新的数值、更新数值所在的区间、使用的线段树数组下标
void update(int val,int des,int id)
{
if(T[id].l==T[id].r && T[id].l==des)
{
T[id].n=val;
return;
}

int mid=(T[id].l+T[id].r)/2;

if(des <= mid)
update(val,des,2*id);
else
update(val,des,2*id+1);

T[id].n=max(T[2*id].n,T[2*id+1].n);
}

int ans;
//查询区间值。三个参数分别为区间下标的最小值、区间下标的最大值、使用的线段树数组下标
void query(int l,int r,int id)
{
if(T[id].l == l && T[id].r == r)
{
ans = max(T[id].n,ans);
return;
}

int mid = (T[id].l + T[id].r)/2;

if(r <= mid)
query(l,r,2*id);
else if(l>mid)
query(l,r,2*id+1);
else
{
query(l,mid,2*id);
query(mid+1,r,2*id+1);
}
}

int main()
{
int n,m;

while(scanf("%d%d",&n,&m) != EOF)
{
build(1,n,1);
//建立线段树。可以在建立线段树的时候将相关信息存入

for(int i=1;i<=n;i++)
{
int tmp;
scanf("%d",&tmp);
update(tmp,i,1);
}

for(int i=1;i<=m;i++)
{
char option[10];
int a,b;

scanf("%s%d%d",&option,&a,&b);

if(option[0] == 'Q')
{
ans = 0;
query(a,b,1);
cout<<ans<<endl;
}
else if(option[0] == 'U')
{
update(b,a,1);
}
}
}
return 0;
}

###HDU 1166 - 敌兵布阵
Online Judge : http://acm.hdu.edu.cn/showproblem.php?pid=1166

这有对区间求和,也有对某个长度为1的特定区间的数值更新。代码与上面提到的例子(HDU 1754)有异曲同工之妙,将代码稍作更改即可。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 51000;

struct seg
{
int l;
int r;
int n;
}T[3*MAXN];

void build(int l,int r,int id)
{
if(l == r)
{
T[id].l=l;
T[id].r=r;
T[id].n=0;
return;
}

int mid = (l+r)/2;

T[id].l=l;
T[id].r=r;
T[id].n=0;

build(l,mid,2*id);
build(mid+1,r,2*id+1);
}

void update(int val,int des,int id)
{
if(T[id].l==T[id].r && T[id].l==des)
{
T[id].n+=val;
return;
}

int mid=(T[id].l+T[id].r)>>1;
if(des <= mid)
update(val,des,2*id);
else
update(val,des,2*id+1);

T[id].n=T[2*id].n+T[2*id+1].n;
}

int ans;

void query(int l,int r,int id)
{
if(T[id].l == l && T[id].r == r)
{
ans += T[id].n;
return;
}
int mid = (T[id].l + T[id].r)>>1;
if(r <= mid)
query(l,r,2*id);
else if(l>mid)
query(l,r,2*id+1);
else
{
query(l,mid,2*id);
query(mid+1,r,2*id+1);
}
}

int main()
{
int t;
scanf("%d",&t);
for(int cas = 1;cas<=t;cas++)
{
int n;
scanf("%d",&n);

build(1,n,1);

for(int i=1;i<=n;i++)
{
int tmp;
scanf("%d",&tmp);
update(tmp,i,1);
}

printf("Case %d:\n",cas);

char str[20];

while(scanf("%s",str))
{
if(strcmp(str,"End")==0)
break;

int a,b;

scanf("%d%d",&a,&b);

if(strcmp(str,"Add")==0)
update(b,a,1);
else if(strcmp(str,"Sub")==0)
update(-b,a,1);
else if(strcmp(str,"Query")==0)
{
ans = 0;
query(a,b,1);
cout<<ans<<endl;
}

}
}
return 0;
}

###POJ 3468 - A Simple Problem with Integers

这题出现了对长度大于1的区间进行数值更新的情况,比上两个例子较为复杂,主要是在对数值的更新问题上。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int MAXN = 100100;

long long sum[4*MAXN];
long long addv[4*MAXN];
//用结构体将区间左值、右值、和、要加的数同时表示亦可,使用起来略复杂

void Pushdown(int id,int diff)
{
if(addv[id])
{
sum[2*id] += addv[id]*(diff-diff/2);
//左儿子的数值 加上 要加的数值乘以左儿子区间长度
sum[2*id+1] += addv[id]*(diff/2);
//右儿子的数值 加上 要加的数值乘以右儿子区间长度
addv[2*id] += addv[id];
//更新左儿子所在区间应加的数值
addv[2*id+1] += addv[id];
//更新右儿子所在区间应加的数值
addv[id]=0;
}
}

void pushup(int id)
{
sum[id]=sum[2*id]+sum[2*id+1];
//将左儿子的数值与右儿子的数值相加更新父节点的数值
}

void build(int l,int r,int id)
{
addv[id]=0;
if(l == r)
{
scanf("%I64d",&sum[id]);
//在树初始化的时候更新树叶值 比 初始化后在插入更新树叶值 复杂度相同
return;
}

int mid = (l+r)/2;
build(l,mid,2*id);
build(mid+1,r,2*id+1);

Pushup(id);
}

void update(int ql,int qr,int val,int l,int r,int id)
{
if(ql <= l && qr >= r)
{
addv[id]+=val;
sum[id]+=(long long)val*(r-l+1);
//增加的数值乘以区间长度
return;
}

Pushdown(id,r-l+1);

int mid = (l+r)/2;
if(ql <= mid)
update(ql,qr,val,l,mid,2*id);
if(qr > mid)
update(ql,qr,val,mid+1,r,2*id+1);

Pushup(id);
}

long long query(int ql,int qr,int l,int r,int id)
{
if(ql <= l && qr >= r)
return sum[id];

Pushdown(id,r-l+1);

long long res = 0;

int mid = (l+r)/2;
if(qr > mid)
res += query(ql,qr,mid+1,r,2*id+1);
if(ql <= mid)
res += query(ql,qr,l,mid,2*id);

return res;
}

int main()
{
int n,q;
while(scanf("%d%d",&n,&q) != EOF && n && q)
{
build(1,n,1);

while(q--)
{
char option[10];
scanf("%s",&option);

if(option[0] == 'Q')
{
int a,b;
scanf("%d%d",&a,&b);
ans = query(a,b,1,n,1);
cout<<ans<<endl;
}
else if(option[0] == 'C')
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
update(a,b,c,1,n,1);
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息