线段树初步(1)

  线段树是一种能够实现快速查询区间,区间修改,单点修改的数据结构。其用途是在O(\log N)查询一个指定区间内的信息,并可在同样的时间复杂度支持更新等操作。

线段树是一个平衡的二元树,所有叶子到根的距离最多只差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;

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值