动态区间最大值最小值区间和查询,支持区间设置,线段树

对区间 [0,N-1] 支持两种操作:

1. update(L, R, v) 将区间[L,R] 的所有值设置为 v;

2. query(L, R) 查询区间 [L, R] 的最大值、最小值、区间和;

利用线段树解决该问题,更新时,对某区间的设置操作不需要分解到其每一个子区间(否则可能要更新每一个叶子节点),只需在该区间上做设置标记;在查询时,遇到有标记的区间,将标记 pushdown 到子区间;

// 用线段树实现动态区间最小值、最大值、区间和查询,对区间A[0..N-1]支持更新操作和查询操作:
// update(int L, int R, int v) 将区间[L,R]所有元素值设置为v
// query(int L, int R ) 查询数组区间[L,R]的最小值、最大值、区间和
public class RangeTree4RangeUpdate {
	
	public static class Result {
		public int minv;
		public int maxv;
		public int sumv;
	}
	
	//N为源数组总长度,可查询区间为[0,N)
	int N = 0;		
	
	//M为最底层叶子节点数目,M = min { x | x = 2^k && x >= N }
	int M = 0;
	
	//线段树的数组表示
	int[] setv = null;	//各结点的设置标记
	int[] sumv = null;	//各结点的区间和
	int[] minv = null;	//各结点的最小值
	int[] maxv = null;	//各结点的最大值
	
	public RangeTree4RangeUpdate(int[] A) {
		build(A);
	}
	
	//构造线段树,从叶子节点递推,复杂度O(N)
	public void build(int[] A) {
		
		if ( A == null || A.length <= 0 )
			throw new IllegalArgumentException();
		
		N = A.length;
		M = calculate(N);
		
		setv = new int[2*M-1]; 
		sumv = new int[2*M-1]; 
		minv = new int[2*M-1]; 
		maxv = new int[2*M-1];
		
		//初始化所有叶子结点
		for ( int i=0; i=0; i-- ) {
			setv[i] = Integer.MAX_VALUE;
			minv[i] = min(minv[2*i+1], minv[2*i+2]);
			maxv[i] = max(maxv[2*i+1], maxv[2*i+2]);
			sumv[i] = sumv[2*i+1] + sumv[2*i+2];
		}
		
	}
	
	// 区间最小值查询
	// L : 待查询区间左边界
	// R : 待查询区间右边界
	public Result query(int L, int R) {
		if ( L > R || L <0 || L >=N || R < 0 || R >=N )
			throw new IllegalArgumentException();
		Result res = new Result();
		res.minv = Integer.MAX_VALUE;
		query(L, R, 0, 0, M-1, res);
		return res;
	}
	
	//区间最小值查询
	//cur : 当前结点在线段树中的编号
	//CUR_L : 当前结点区间的左边界
	//CUR_R : 当前结点区间的右边界
	private void query(int L, int R, int c, int CL, int CR, Result res) {
		
		if ( setv[c] != Integer.MAX_VALUE ) {
			res.sumv += setv[c] * (min(R,CR)-max(L,CL)+1);
			res.maxv = max(res.maxv, maxv[c]);
			res.minv = min(res.minv, minv[c]);
		} else if ( L <= CL && CR <= R ) {
			//待查询区间完全覆盖当前结点区间
			res.maxv = max(res.maxv, maxv[c]);
			res.minv = min(res.minv, minv[c]);
			res.sumv += sumv[c]; 
		} else {
			int CM = ( CL + CR ) / 2;
			//查询区间与左半区间有重叠
			if ( L <= CM ) query(L, R, 2*c+1, CL, CM, res);
			//查询区间与右半区间重叠
			if ( R > CM ) query(L, R, 2*c+2, CM+1, CR, res);
		}
		
	}
	
	//更新一个区间,将 [L,R] 区间所有值更新为 v
	public void update(int L, int R, int v) {
		update(L,R,v,0,0,M-1);
	}
	
	//更新区间最小值
	//c : 当前结点在线段树中的编号
	//CL : 当前结点区间的左边界
	//CR : 当前结点区间的右边界
	private void update(int L, int R, int v, int c, int CL, int CR) {
		if ( L == R ) {
			setv[c] = sumv[c] = maxv[c] = minv[c] = v;
		} else {
			if ( L <= CL && R >= CR ) {
				setv[c] = v;
			} else {
				pushdown(c);
				int CM = (CR+CL)/2, lc = 2*c+1, rc = 2*c+2;
				if ( L <= CM ) update(L, R, v, lc, CL, CM); else maintain(lc, CL, CM);
				if ( R > CM ) update(L, R, v, rc, CM+1, CR); else maintain(rc, CM+1, CR);
			}
			maintain(c,CL,CR);
		}
	}
	
	// 将当前结点的设置标记下移
	private void pushdown(int c) {
		if ( setv[c] != Integer.MAX_VALUE ) {
			setv[2*c+1] = setv[c];
			setv[2*c+2] = setv[c];
			setv[c] = Integer.MAX_VALUE;
		}
	}
	
	// 计算当前结点区间的最小最大值及区间和
	private void maintain(int c, int CL, int CR) {
		int lc = 2*c+1, rc = 2*c+2;
		if ( setv[c] == Integer.MAX_VALUE ) {
			sumv[c] = sumv[lc] + sumv[rc];
			maxv[c] = max(maxv[lc],maxv[rc]);
			minv[c] = min(minv[lc],minv[rc]);

		} else {
			sumv[c] = setv[c]*(CR-CL+1);
			maxv[c] = minv[c] = setv[c];
		}
	}
	
	// 计算较小值
	private int min(int x, int y) {
		return (xy) ? x:y;
	}
	
	// 计算最底层的叶子结点数目
	private int calculate(int n) {
		int y = 1;
		while ( y < n ) {
			y <<= 1;
		}
		return y;
	}
	
	// 测试
	public static void main(String[] args) {
		int[] v = { 7, 8, 9, 5, 6, 4, 3, 2 };
		RangeTree4RangeUpdate inst = new RangeTree4RangeUpdate(v);
		for ( int i=0; i


你可能感兴趣的:(算法,数据结构)