1.什么是线段树
线段树是一种高级数据结构,也是一种二叉树结构。它能够高效的处理区间修改查询等问题。线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实际应用时一般还要开4N的数组以免越界,因此有时需要离散化让空间压缩。
2.线段树的结构
3.线段树的实现
package 数据结构.树; public class SegmentTree<E> {
private E[] data;
private E[] tree;
private Merger<E> merger;
public SegmentTree(E[] arr, Merger<E> merger) {
this.merger = merger;
data = (E[]) new Object[arr.length];
System.arraycopy(arr, 0, data, 0, arr.length);
tree = (E[]) new Object[arr.length * 4];
buildSegmentTree(0, 0, arr.length - 1);
}
private void buildSegmentTree(int treeIndex, int l, int r) {
if (l == r) {
tree[treeIndex] = data[l];
return;
}
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = right(treeIndex);
int mid = l + (r - l) / 2;
buildSegmentTree(leftTreeIndex, l, mid);
buildSegmentTree(rightTreeIndex, mid + 1, r);
tree[treeIndex] = merger.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
}
public int getSize() {
return data.length;
}
public E get(int index) {
if (index < 0 || index >= data.length) {
throw new IllegalArgumentException();
}
return data[index];
}
private int leftChild(int index) {
return index * 2 + 1;
}
private int right(int index) {
return index * 2 + 2;
}
public E query(int queryL, int queryR) {
if (queryL < 0 || queryR >= data.length || queryL >= data.length || queryR < 0 || queryL > queryR) {
throw new IllegalArgumentException();
}
return query(0, 0, data.length - 1, 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 i = leftChild(treeIndex);
int j = right(treeIndex);
if (queryL >= mid + 1) {
return query(treeIndex, mid + 1, r, queryL, queryR);
} else if (queryR <= mid) {
return query(treeIndex, j, mid, queryL, queryR);
}
E leftResult = query(i, l, mid, queryL, mid);
E rightResult = query(j, mid + 1, r, mid + 1, queryR);
return merger.merge(leftResult, rightResult);
}
public void set(int index, E e) {
if (index < 0 || index >= data.length) {
throw new IllegalArgumentException();
}
data[index] = e;
set(0, 0, data.length - 1, e, index);
}
private void set(int treeIndex, int l, int r, E e, int index) {
if (l == r) {
tree[treeIndex] = e;
return;
}
int mid = l + (r - l) / 2;
int i = leftChild(index);
int j = right(index);
if (index >= mid + 1) {
set(j, mid + 1, r, e, index);
} else {
set(i, l, mid, e, index);
}
tree[treeIndex] = merger.merge(tree[i], tree[j]);
} }
4.线段树的应用
https://leetcode.cn/problems/range-sum-query-mutable/description/
题解代码:
class NumArray {
Node[] tr;
class Node {
int l, r, v;
Node(int _l, int _r) {
l = _l; r = _r;
}
}
void build(int u, int l, int r) {
tr[u] = new Node(l, r);
if (l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
void update(int u, int x, int v) {
if (tr[u].l == x && tr[u].r == x) {
tr[u].v += v;
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) update(u << 1, x, v);
else update(u << 1 | 1, x, v);
pushup(u);
}
int query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) return tr[u].v;
int mid = tr[u].l + tr[u].r >> 1;
int ans = 0;
if (l <= mid) ans += query(u << 1, l, r);
if (r > mid) ans += query(u << 1 | 1, l, r);
return ans;
}
void pushup(int u) {
tr[u].v = tr[u << 1].v + tr[u << 1 | 1].v;
}
int[] nums;
public NumArray(int[] _nums) {
nums = _nums;
int n = nums.length;
tr = new Node[n * 4];
build(1, 1, n);
for (int i = 0; i < n; i++) update(1, i + 1, nums[i]);
}
public void update(int index, int val) {
update(1, index + 1, val - nums[index]);
nums[index] = val;
}
public int sumRange(int left, int right) {
return query(1, left + 1, right + 1);
}
}