线段树
线段树是算法竞赛中常用的用来维护 区间信息 的数据结构。
线段树可以在 的时间复杂度内实现单点修改、区间修改、区间查询(区间求和,求区间最大值,求区间最小值)等操作。
线段树维护的信息,需要满足可加性,即能以可以接受的速度合并信息和修改信息,包括在使用懒惰标记时,标记也要满足可加性(例如取模就不满足可加性,对 取模然后对 取模,两个操作就不能合并在一起做)。
1.区间求和的线段树
若只需要求区间和,而不需要改变区间值 —> 使用前缀和
若在求区间和的基础上,还要修改区间数组的值,为了更快的维护一个前缀和数组 —-> 线段树
线段树实际就是在前缀和的基础上使用二分构建二叉搜索树,提高修改区间值的时间
线段树一般支持三个基本操作:构建树,修改某元素的值,查询区间和
如图:原数组和它对应的线段树
修改下标为4的元素为6后的线段树:
一个区间求和线段树的Java基本模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| public class SegmentTree { private int[] data; private int[] tree;
public SegmentTree(int[] data) { this.data = data; tree = new int[data.length*4]; buildTree(0,0,data.length-1); }
private void buildTree(int treeIndex, int l, int r){ if(l == r){ tree[treeIndex] = data[l]; return; } int leftTreeIndex = 2 * treeIndex + 1; int rightTreeIndex = 2 * treeIndex + 2; int mid = (l+r)/2; buildTree(leftTreeIndex, l, mid); buildTree(rightTreeIndex, mid + 1, r); tree[treeIndex] = tree[leftTreeIndex] + tree[rightTreeIndex]; }
public void update(int treeIndex, int l, int r,int index,int val){ if(l == r){ data[index] = val; tree[treeIndex] = val; return; } int mid = (l+r)/2; int leftTreeIndex = 2 * treeIndex + 1; int rightTreeIndex = 2 * treeIndex + 2; if(index >= l && index <= mid){ update(leftTreeIndex, l, mid,index,val); } else{ update(rightTreeIndex, mid+1, r,index,val); } tree[treeIndex] = tree[leftTreeIndex] + tree[rightTreeIndex]; }
public int query(int treeIndex, int l, int r,int queryL,int queryR){ System.out.println(l + " " + r); if(queryR < l || queryL > r) return 0; else if(queryL <= l && r <= queryR) return tree[treeIndex]; else { int mid = l + (r - l) / 2; int leftTreeIndex = 2 * treeIndex + 1; int rightTreeIndex = 2 * treeIndex + 2; int sum_left = query(leftTreeIndex, l, mid, queryL, queryR); int sum_right = query(rightTreeIndex, mid + 1, r, queryL, queryR); return sum_left + sum_right; } }
}
|
若要使用更复杂的数据结构和运算规则,修改:
1 2 3 4
| private int[] data; private int[] tree; 和 tree[treeIndex] = tree[leftTreeIndex] + tree[rightTreeIndex];
|
参考资料:
灯笼大神讲线段树
线段树百科