C++实现线段树
对于一给定的数组a,若要计算「长度为n的连续元素和:a[i+1] + …+a[i+n] 」时间复杂度为O(n);若要修改某一位上的元素值,时间复杂度为O(1)。
若创建一个新数组b记录「前n位的累加和」,则查询「给定长度的元素和:a[i+1] + …+a[i+n] = b[i+n] - b[i]」的时间复杂度为O(1);修改某一位的元素值的时间复杂度变为O(n)。
当需要频繁进行两种操作时,时间开销较大。因此我们希望有一个平均时间复杂度较低的算法/数据结构来实现。
事实上,线段树的两种操作时间复杂度均为O(log n)
图形解释
下图为一个长度为5的数组 a[61, 3, 35, 5, 50] 生成的线段树,叶节点存储原数据,非叶子结点存储一定范围内节点的和。
如:
154 = a[0]+ a[1] +a[2]+a[3]+a[4]
55 = a[3] + a[4]
64 = a[0] + a[1]
3 = a[1]
这是个sum_segment_tree的样例,事实上ST中的求和运算可以替换为min、max等运算。
代码
#include <iostream>
#include <vector>
class segment_tree{
//计算任意长度的连续元素和get_sum,和修改任意元素update_node 的时间复杂度均为 O(log(n))
public:
std::vector<int> data;
std::vector<int> tree;
//构造函数
explicit segment_tree(std::vector<int>& input){
data = input;
tree = std::vector<int> (4*data.size(),int(0)); //四倍大小
create_node(0 ,data.size() -1, 0);
}
//修改初始第i位元素值为value
void update_node(int i, int value){
int delta = value - data[i];
data[i] = value;
int left = 0,right = data.size()-1,mid,dex = 0;
do{
tree[dex] += delta;
mid = (left + right)/2;
if(i<=mid){
right = mid;
dex = dex*2 + 1;
}else{
left = mid + 1;
dex = dex*2 +2;
}
}while(right != left);
tree[dex] += delta;
}
//获取i到j的累加和 data[i] + data[i+1] + ... + data[j]
int get_sum(int i, int j){
int left=0,right=data.size()-1;
return get_sum(i,j,left,right,0);
}
private:
//线段树节点赋值 线段上下坐标 线段树实际索引位置
void create_node(int min_dex, int max_dex, int tree_dex){
if(min_dex == max_dex){
tree[tree_dex] = data[max_dex];
return;
}
int mid = (min_dex + max_dex)/2;
create_node(min_dex,mid,2*tree_dex +1);
create_node(mid +1,max_dex,2*tree_dex +2);
tree[tree_dex] = tree[2*tree_dex+1] + tree[2*tree_dex +2];
}
//递归获取累加和,深度优先,递归
int get_sum(int i,int j, int left, int right,int dex){
if(i > j)
return 0;
if(i == left && j ==right)
return tree[dex];
int mid = (left + right)/2;
return get_sum(i, std::min(j,mid), left, mid,2*dex +1) +
get_sum(std::max(i,mid+1), j, mid+1, right,2*dex +2);
}
};
int main() {
std::vector<int> f = {61,3,35,5,50};
segment_tree t = segment_tree(f);
for(int i : t.tree){
printf("%d\t", i);
}
t.update_node(4,10);
std::cout << std::endl;
for(int i : t.tree){
printf("%d\t", i);
}
printf("\n%d",t.get_sum(1,3));
return 0;
}
执行main测试后,结果如下:
154 99 55 64 35 5 50 61 3 0 0 0 0 0 0 0 0 0 0 0
114 99 15 64 35 5 10 61 3 0 0 0 0 0 0 0 0 0 0 0
43