线段树(Segment Tree)是一种二叉搜索树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。对于线段树中的每一个非叶子节点[a,b],它的左子树表示的区间为[a,(a+b)/2],右子树表示的区间为[(a+b)/2+1,b]。因此线段 树是平衡二叉树。叶节点数目为N,即整个线段区间的长度。使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。
如下图:
(1)采用静态方法构建线段树,即用数组来做,左子树为 2*i,右子树为 2*i+1。按照保守估计,数组空间一般开N*4,用到的空间实质上是N*2-1,但是考虑到 中间 会有一些空间没有使用,特别是最底层的。粗略地想,假设最底层只有最后两个 空间有用到,那么这一层前面的空余空间就约为N*2了。所以N*4是足够的。
(2)这部分一般来说涉及到下面几个函数:pushUp(由下往上更新修改后的数据), build(初始化线段树),update(修改叶子结点的数据),query(查询区间数据)。(3)线段树本身并不保存当前区间具体是什么,而是由函数传递两个变量l, r 来记录 当前的具体区间,这样做可节省大量空间开销,详细参见代码。
hdoj 1166 敌兵布阵: http://acm.hdu.edu.cn/showproblem.php?pid=1166
#include <stdio.h>
#define N 500010 //数据范围
int sum[N << 2];//保存各个范围的sum值
//检查下层数据,更新当前的sum值
void pushup(int cur) // cur 表示当前数组的实际下标。
{
sum[cur] = sum[cur << 1] + sum[cur << 1 | 1];//左右子树之和
}
// [l, r] 表示当前数组存储的值的区间
void build_segTree(int left, int right, int cur)
{
if(left == right) //初始化线段树
{
scanf("%d", &sum[cur]);
return;
}
int m = (left+right) >> 1;
build_segTree(left, m, cur<<1);
build_segTree(m + 1, right, cur << 1 | 1);
pushup(cur);//更新当前的sum
}
// 更新范围i的值,val 表示需要增加的值
void update(int i, int val, int left, int right, int cur)
{
//更新范围i的值,并向上传递sum
if( left == right)
{
sum[cur] += val;
return ;
}
int m = (left + right) >> 1;
if(i <= m)
update(i, val, left, m, cur << 1);
else
update(i, val, m+1, right, cur << 1 | 1);
pushup(cur); //传递sum值
}
//L 至 R 表示需要查询的区间
int query(int L, int R, int left, int right, int cur)
{ //当前节点l,r完全包含在查询区间L R内
if(L <= left && R >= right )
return sum[cur];
int result =0;
int m = (left + right) >> 1;
if(L <= m)
result += query(L, R, left, m, cur << 1);
if(R > m)
result += query(L, R, m+1, right, cur << 1 | 1);
return result;
}
int main()
{
//freopen("in.txt", "r", stdin);
int t, n;//样例数,营地数
int ncase = 1; //当前例子编号
int v1, v2;
scanf("%d", &t);
while(t--)
{
scanf("%d", &n);
build_segTree(1, n, 1);// 1,n 输入n个数
printf("Case %d:\n", ncase++);
char qe[12];
while(scanf("%s", qe) != EOF)
{
if(qe[0] == 'E')
break;
scanf("%d %d", &v1, &v2);
if(qe[0] == 'A')
update(v1, v2, 1, n, 1);
else if(qe[0] == 'S')
update(v1, -v2, 1, n, 1);
else if(qe[0] == 'Q')
printf("%d\n", query(v1, v2, 1, n, 1));
}
}
return 0;
}
hdoj 1754 I Hate It: http://acm.hdu.edu.cn/showproblem.php?pid=1754
代码如下:
#include <stdio.h>
#include
#define M 200005
int max[M<<2];//构建最高成绩线段树
void pushup(int cur)
{
//更新 max[cur] 的值
max[cur] = max[cur << 1] >= max[cur << 1 | 1] ? max[cur << 1] : max[cur << 1 | 1];
}
void build(int l, int r, int cur)
{
if(l == r) //初始化线段树
{
scanf("%d", &max[cur]);
return ;
}
int m = (l+r)>>1;
build(l, m, cur << 1);
build(m+1, r, cur << 1 | 1);
pushup(cur);
}
void update(int i, int val , int l, int r, int cur)
{
if(l == r) //更新 i 的值 为 val
{
max[cur] = val;
return ;
}
int m = (l+r)>>1;
if(i <= m)
update(i, val, l, m, cur << 1);
else
update(i, val, m+1, r, cur << 1 |1);
pushup(cur);
}
int query(int L, int R, int l, int r, int cur)
{
// //返回[L, R]的最高成绩
if(L<=l && R>=r)
return max[cur];
int m=(l+r)>>1;
int ma=0, mb=0;
if(L <= m)
ma = query(L, R, l, m, cur << 1);
if(R > m)
mb = query(L, R, m+1, r, cur << 1 |1);
return ma>mb ? ma:mb;
}
int main()
{
freopen("in.txt", "r", stdin);
int n,m;//人数,操作次数
char c;
int a, b;
while(EOF != scanf("%d %d", &n, &m))
{
build(1, n, 1);
while(m--)
{
getchar();// 注意把之前的换行符读取
scanf("%c %d %d", &c, &a, &b);
if(c == 'U')
update(a, b, 1, n, 1);
else if(c == 'Q')
printf("%d\n", query(a, b, 1, n, 1));
}
}
return 0;
}