线段树

线段树Segment Tree)是一种二叉搜索树,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。对于线段树中的每一个非叶子节点[a,b],它的左子树表示的区间为[a,(a+b)/2],右子树表示的区间为[(a+b)/2+1,b]。因此线段 树是平衡二叉树。叶节点数目为N,即整个线段区间的长度。使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。

  如下图:

 线段树_第1张图片

(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;
}

你可能感兴趣的:(算法,线段树,hdoj,1166,hdoj1754)