线段树的学习记录

线段树的学习记录

 

1、区间查询:询问某段区间的某些性质(极值、求和、etc

2、区间更新:某些操作影响了某些区间(统一加一个值)

3、三个问题: 更新点,查询区间

更新区间,查询点

更新区间,查询区间

 

例如:一个长度为N的数组a[1]~a[n]:

我们每次对该数组有一些操作:

1、修改数组中某个元素的值

1,5,4,1,6---(a[2]=3)---> 1,3,4,1,6

2、询问数组中某段区间的最大值

1,5,4,1,6---(max(1,4)=?)---> 5

3、询问数组中某段区间的和

1,5,4,1,6---(sum(3,5)=?)---> 11

 

如果只有一次询问?

枚举相应区间内的元素,输出答案   O(N)

更多的询问?

Q次询问,O(NQ)  That's too SLOW!

线段树——在O(log2N)的时间内完成每次操作 O(Qlog2N)

Less than 1 second when N=Q=100000


线段树的本质是一棵二叉树,不同于其它二叉树,线段树的每一个节点记录的是一段区间的信息。

对于任一非叶子节点,若该区间为[L,R],则
左儿子为[L,(L+R)/2]
右儿子为[(L+R)/2+1,R]


每一个节点记录的信息?
struct Tree
{
   int left,right;     //区间的端点
   int max,sum;   //视题目要求而定
};


线段树——代码实现(建树)

void build(int id,int l,int r){
		tree[id].left=l; tree[id].right=r;
		if (l==r)
		{
			tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
			tree[id].max=max(tree[id*2].max,tree[id*2+1].max;
		}
		else
		{
			int mid=(l+r)/2;
			build(id*2,l,mid);
			build(id*2+1,mid+1,r);
			tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
			tree[id].max=max(tree[id*2].max,tree[id*2+1].max;
		}
	}

如果原数组从a[1]~a[n],调用build(1,1,n)即可。


线段树——代码实现(更新)

更新某个点的数值,并维护相关点的信息

void update(int id,int pos,int val){
		if (tree[id].left==tree[id].right)
		{
			tree[id].sum=tree[id].max=val;
		}
		else
		{
			int mid=(tree[id].left+tree[id].right)/2;
			if (pos<=mid) update(id*2,pos,val);
			else update(id*2+1,pos,val);
			tree[id].sum=tree[id*2].sum+tree[id*2+1].sum;
			tree[id].max=max(tree[id*2].max,tree[id*2+1].max)
		}
	}

若更新a[k]的值为val,调用update(1,k,val)即可。


查询某区间内元素的和或最大值(以总和为例)

void query(int id,int l,int r){
		if (tree[id].left==l&&tree[id].right==r)
			return tree[id].sum; //询问总和
		else
		{
			int mid=(tree[id].left+tree[id].right)/2;	
			if (r<=mid) return query(id*2,l,r);
			else if (l>mid) return query(id*2+1,l,r)
			else
			   return query(id*2,l,mid)+query(id*2+1,mid+1,r);
		}
	}

调用query(1,l,r)即可查询[l,r]区间内元素的总和。

线段树——空间复杂度

更新操作:由于总是一条路径从根到某个叶子,而树的深度为log2N,因而为O(log2N)

查询操作:每层被访问的节点不超过4个,因而同样为O(log2N)


线段树——空间复杂度

设长度为N的数组在线段树中,编号最靠右的节点编号为F(N)
若N=2n, F(N)=2(n+1)
若N=2(n+1),F(N)=2(n+2)


因而对于2n<=N<=2(n+1),有
2(n+1)<=F(N)<=2(n+2)
F(N)<=4*N

1、线段树可以做很多很多与区间有关的事情……
2、空间复杂度~O(N*4),每次更新和查询操作的复杂度都是O(logN)。


3、在更新和查询区间[l,r]的时候,为了保证复杂度是严格的O(logN)必须在达到被[l,r]覆盖的区间的结点时就立即返回。而为了保证这样做的正确性,需要在这两个过程中做一些相关的“懒”操作。


“懒操作”在更新区间的有关问题上至关重要。

你可能感兴趣的:(线段树的学习记录)