对区间 [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