数据结构之线段树(点修改)

首先,明确一点,线段树并不是什么神奇的东西,只是巧妙地利用了分治的思想,它用于区间的动态查询问题~

线段树由很多下面这样的单元组合起来的(我懒地画图了),其中M=L+(R-L)/2(当然也可以写成(L+R)/2,但我们应该养成用前面那种方式找中值的习惯,当L和R特别大的时候,L+R有可能溢出,不过线段树上一般没什么问题),使用线段树的前提是你求的东西必须满足区间加法

   

 

顺便提一下:

如果知道了[l, m] ,[m + 1, r]的信息 就可以知道[l, r]的信息 这个就叫做满足区间加法,也就是上图的【L,M】 + 【M+1,R】 = 【L,R】

如果知道了[1, r] ,[1, l - 1]的信息 就可以知道[l, r]的信息 这个就叫做满足区间减法,也就是上图的【L,R】 - 【L,M】 = 【M+1, R】

举个栗子,你要求最小值,用min(a, b)表示【a, b】区间最小值,那么:min(L,R)=min( min(L,M), min(M+1, R) )——这很好理解的吧~

同样,还有区间最大值,区间和等等~

差不多意思明白了,现在是具体实现问题了,给一个数组a[],数组大小为n,那么如何建树?

首先,一般也使用数组来存储线段树,设为segTree[ ]吧,即:

也就是满足:

方框里的值对应的是是线段树数组的索引,也就是标号,值得注意的是,虽然强调线段树每一个节点与区间相关,但只有根结点的区间的左右端点被记录下来了(因为任何一个节点表示的区间我们都可以从根结点不断二分得到,复杂度只有log n)。

那么问题来了,节点框框里面的 i 什么的是索引,那segTree[i]到底是什么呢?显然这就和你要求什么有关了,比如你要求最大值,那么segTree[i] = max( segTree[2*i+1], segTree[2*i+2]),也就是左右子节点的最大值——这不难理解。如果既要求最大值,又要求最小值,还要求区间和,那怎么办?——很简单啊,线段树节点别用int,而是用一个结构体,一个存最大值,一个存最小值,还有一个存区间和就OK了~

好像有什么不对。。。左右子节点的最大值?它是叶结点怎么办?那必然存的就是a[ ]数组了~

比如:

这样的,叶子结点分别就存储a[0]、a[1]、a[2]、a[3]。当然,这也并不能太死板了,比如你要看去区间里多少个1,那叶子结点肯定就存1或者0了,说存a[]只是大多数情况~还有一个问题,segTree[]要开多大的呢?肯定不会是无穷大的~事实上,我们通常开a[]的4倍大小,这样可以保证不溢出,要证明挺简单的,a[]有n个元素,也就是线段树有n个叶子节点,树高,这是<=(int)log n + 1的,也就是最多有(int)log n+2层,然后等比数列2^(k-1)求前(int)log n+2项和了,大概就是4n了~

建树说完了(具体代码最后一起贴),然后就是查询了,查询没什么好说的,就从根结点往下面找啊,O(log n)复杂度,具体的看下面代码就懂了~

然后,就是开篇第一句话里说的动态了——线段树是可修改的,这可就超级螺旋棒棒糖了~如果不涉及修改,不用线段树也不是不可以,万物皆可预处理——但是如果加上修改的话,修改一次就要重新处理一次,这就很伤了~对于线段树,修改也是很简单的(正如文章标题所说,这里只涉及点的修改,区间的修改下次再说~),首先由根结点向下递归找到要修改的结点,然后从该节点回溯更新祖先节点,也是O(log n)的复杂度——耐斯~~

今天之所以写这篇博客,是因为我们Java课程训练题出现了这个(虽然那一题数据水到暴力可直接过。。。),我就用Java撸了一波线段树代码,就来记录一下啦~本来想再撸一段C++的,后来还是放弃了,因为我觉得,即使不会Java,也能看那几个函数,毕竟和C++基本没有区别~

/*
 *【问题描述】老师想知道从某某同学当中,分数最高的是多少,现在请你编程模拟老师的询问。当然,老师有时候需要更新某位同学的成绩.
 *【输入形式】输入包括多组测试数据。每组输入第一行是两个正整数N和M(0 < N <= 30000,0 < M < 5000),分别代表学生的数目和操作的数目。 学生ID编号从1编到N。第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩,接下来又M行,每一行有一个字符C(只取‘Q’或‘U’),和两个正整数A,B,当C为'Q'的时候, 表示这是一条询问操作,他询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少,当C为‘U’的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。
 *【输出形式】对于每一次询问操作,在一行里面输出最高成绩.
 *【样例输入】
 5 7 
 1 2 3 4 5 
 Q 1 5
 U 3 6
 Q 3 4
 Q 4 5
 U 4 5
 U 2 9
 Q 1 5
 *【样例输出】5 6 5 9
 */

import java.util.*;

public class StudentGrade{
	//最大值线段树
	static final int INF = -0x3f3f3f3f;
	static void build(int rt, int[] segTree, int[] a, int istart, int iend){
		if(istart == iend){
			segTree[rt] = a[istart];
			return;
		}
		build((rt<<1)+1, segTree, a, istart, (istart+iend)/2);
		build((rt<<1)+2, segTree, a, (istart+iend)/2 + 1, iend);
		segTree[rt] = Math.max(segTree[(rt<<1)+1], segTree[(rt<<1)+2]);
	}
	//查询[ql, qr]最大值
	static int query(int[] segTree, int rt, int L, int R, int ql, int qr){
		int M = L + (R-L)/2, ans = INF;
		if(ql <= L && R <= qr)	return segTree[rt];
		if(ql <= M)	ans = Math.max(ans, query(segTree, (rt<<1)+1, L, M, ql, qr));
		if(qr > M)	ans = Math.max(ans, query(segTree, (rt<<1)+2, M+1, R, ql, qr));
		return ans;
	}
	//更新,a[p] = v
	static void update(int rt, int[] segTree, int L, int R, int p, int v){
		int M = L + (R-L) / 2;
		if(L == R)	segTree[rt] = v;
		else{
			if(p <= M)	update((rt<<1)+1, segTree, L, M, p, v);
			else		update((rt<<1)+2, segTree, M+1, R, p, v);
			segTree[rt] = Math.max(segTree[(rt<<1)+1], segTree[(rt<<1)+2]);
		}
	}
	public static void main(String[] args){
		Scanner sc = new Scanner(System.in);
		while(sc.hasNext()){
			int N = sc.nextInt(), M = sc.nextInt();
			int[] segTree = new int[N<<2];
			int[] a = new int[N];
			for(int i = 0; i < N; ++ i)	a[i] = sc.nextInt();
			build(0, segTree, a, 0, N-1);
			for(int i = 0; i < M; ++ i){
				String s = sc.next();
				if(s.equals("Q")){
					int ql = sc.nextInt(), qr = sc.nextInt();
					int ans = query(segTree, 0, 0, N-1, ql-1, qr-1);
					System.out.println(ans);
				}
				else{
					int p = sc.nextInt(), v = sc.nextInt();
					update(0, segTree, 0, N-1, p-1, v);
				}
			}
		}
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值