一. 线段树的特点
-
平衡二叉树:最大的深度 和最小的深度 差最大为1。
-
一种二叉搜索树。它将一段区间划分为若干单位区间,每一个节点都储存着一个区间。它功能强大,支持区间求和,区间最大值,区间修改,单点修改等操作。
处理问题: 区间查询 ,区间更新 -
线段树 是以空间换时间的处理,开启4N的空间
如果区间有n个元素,数组表示需要有多少节点? 4n的空间,我们的线段树不考虑添加元素,即空间固定,使用4n的静态空间即可。可能有浪费,最坏情况,浪费2n-1的空间,空间换时间。0 层 : 1
1 层 : 2
2 层 : 4
3 层 : 8
。。。
h 层 : 2^h -1
对满二叉树: h层,一共·2^h -1 个节点(大约2 ^ h)
最后一层 (h-1)层,有2^(h-1) -1 个节点
最后一层的节点数大致等于前面所有层节点之和。
如果 n = 2^k ,只需要2n区间
最坏情况,如果n = 2^k + 1 ,需要4n的空间。
如果使用链表,可以避免浪费空间。
二. 线段树的初始化
伪代码如下:
/**
* 在treeIndex的位置创建表示区间[l...r]的线段树
* 递归实现 这里有些不好理解,需要手动画画
*
* @param treeIndex
* @param l
* @param r
*/
private void buildSegmentTree(int treeIndex, int l, int r) {
if (l == r) {
tree[treeIndex] = data[l];
return;
}
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
//创建左右子树
int mid = l + (r - l) / 2;
//l,mid; mid+1,r;递归
buildSegmentTree(leftTreeIndex, l, mid);
buildSegmentTree(rightTreeIndex, mid + 1, r);
tree[treeIndex] = merge.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
}
三. 区间查询
伪代码如下:
/**
* 在以treeIndex为根的线段树中【l,r】的范围里,搜索区间【queryL,queryR】的值
*
* @param treeIndex 初始为0
* @param l 初始为0
* @param r 初始为数组最大索引
* @param queryL 左区间
* @param queryR 右区间
* @return
*/
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 leftResult = query(leftTreeIndex, l, mid, queryL, mid);
E rightResult = query(rightTreeIndex, mid + 1, r, mid + 1, queryR);
return merge.merge(leftResult, rightResult);
}
}
四. 区间更新
都是采用递归的方式更新
//在以treeIndex为跟的线段树种更新index的值为e 类似二分搜索树中的更新
public void set(int treeIndex, int l, int r, int index, E e) {
if (l == r) {
tree[treeIndex] = e;
return;
}
int mid = l + (r - l) / 2;
int leftTreeIndex = leftChild(treeIndex);
int rightTreeIndex = rightChild(treeIndex);
if (index >= mid + 1) {
set(rightTreeIndex, mid + 1, r, index, e);
} else {
set(leftTreeIndex, l, mid, index, e);
}
tree[treeIndex] = merge.merge(tree[leftTreeIndex], tree[rightTreeIndex]);
}