数据结构(一)-线段树与并查集
线段树 - SegmentTree
为什么要使用线段树?
—— Range query
一类典型问题,染色问题。
每次在区间 「a,b」中挑选一个颜色进行粉刷。 求经过m次粉刷操作后,区间「i,j」上有多少种颜色?
线段树对区间查询有一些特殊设计,所以也称为区间树。
线段树是一棵二叉树,每个节点表示一个区间。 父节点是两个子节点的超集。
线段树不是完全二叉树,是平衡二叉树
查询的时间复杂度为 O(log N)。
递归创建线段树的过程, buildSegmentTree
public class SegmentTree<E> {
private E[] data;
private E[] tree;
private Merger<E> merger;
public SegmentTree(E[] arr, Merger merger){
data = (E[]) new Object[arr.length];
for(int i = 0; i < arr.length; i ++){
data[i] = arr[i];
}
tree = (E[]) new Object[arr.length * 4];
this.merger = merger;
buildSegmentTree(0, 0, data.length-1);
}
// 在treeIndex 位置创建表示区间为[l...r]的线段树
private void buildSegmentTree(int treeIndex, int l, int r){
if(l == r){
tree[treeIndex] = data[l];
return;
}else {
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
int mid = l + (r-l)/2;
buildSegmentTree(leftTreeIndex, l, mid);
buildSegmentTree(rightTreeIndex, mid+1, r);
// 综合两个孩子节点的信息, 使父节点是两个区间的加总结果。
tree[treeIndex] = merger.merge( tree[leftTreeIndex], tree[rightTreeIndex]);
}
}
```
注意, 区间合并的归则不一定是求和,也可以是求最大,计数,去重计数。
所以这里要放宽merge操作的逻辑,传入一个融合器接口——merger。
查询函数的递归表示:
```java
// 返回区间[queryL, queryR] 的值
public E query(int queryL, int queryR){
if(queryL < 0 || queryR < 0 || queryL >= data.length || queryR >= data.length)
throw new IllegalArgumentException("index error");
return query(0, 0, data.length -1, queryL, queryR);
}
// 以treeId 为根的线段树「L...R」的范围内,搜索子区间[queryL, queryR]的值
private E query(int treeIndex, int l, int r, int queryL, int queryR){
if(l == queryL && r == queryR){
return tree[treeIndex];
}
int mid = l + (r-l) /2;
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
if(queryL >= mid + 1){
// 与左子树无关
return query(rightTreeIndex, mid+1, r, queryL, queryR);
} else if(queryR <= mid){
// 与右子树无关
return query(leftTreeIndex, l, mid, queryL, queryR);
} else {
// 与左子树 右子树都有关系
E lResult = query(leftTreeIndex, l, mid, queryL, mid);
E rResult = query(rightTreeIndex, mid+1, r, mid+1, queryR);
return merger.merge(lResult, rResult);
}
}
```
空间复杂度, 4N。
时间复杂度,
- 创建树 O(N)
- 更新 O(log N)
- 查询 O(log N)
# 并查集 - Union Find Set