线段树(Segment Tree)是一种用于解决区间查询问题的数据结构。它可以在对数时间复杂度内完成区间的查询、更新和查询区间统计值等操作。
线段树的构建是通过递归的方式实现的。我们将区间分成一部分,然后递归地构建左子树和右子树,最后将这两个子树合并成一个线段树。
线段树的节点保存了区间的起始位置、结束位置和统计值等信息。每个节点有一个左孩子和右孩子,分别对应区间的左半部分和右半部分。
线段树的查询操作是通过递归地查找查询区间所在的位置来实现的。当查询区间完全包含当前节点的区间时,返回当前节点的统计值;当查询区间与当前节点的区间没有交集时,返回一个特定的值(如0);否则递归地查询左子树和右子树。
线段树的更新操作是通过递归地更新节点的统计值来实现的。当更新的位置在当前节点的区间中时,更新当前节点的统计值;否则递归地更新左子树和右子树。
线段树的查询区间统计值操作是通过递归地将区间分成几部分来实现的。当查询区间完全包含当前节点的区间时,返回当前节点的统计值;当查询区间与当前节点的区间没有交集时,返回一个特定的值(如0);否则递归地将查询区间分成两部分分别查询左子树和右子树,并将两者的结果进行合并。
线段树的时间复杂度是O(logn),其中n是区间的长度。因为每个节点都需要递归访问左子树和右子树,所以总的递归次数是O(logn),而每次递归的时间复杂度是O(1),所以总的时间复杂度是O(logn)。
线段树可以应用于解决各种区间查询问题,例如区间最大值、区间最小值、区间和等。它在处理区间查询问题上有着较好的效果,并且可以通过合并两个子树的统计值来快速完成查询和更新操作,因此得到了广泛的应用。
代码:
下面是一个使用线段树实现单点修改和区间查询的C++代码模板:
#include <iostream>
#include <vector>
using namespace std;
// 线段树节点
struct Node {
int start, end; // 表示节点所代表的区间 [start, end]
int sum; // 表示节点所代表的区间的和
Node *left, *right; // 左子节点和右子节点
Node(int start, int end): start(start), end(end), sum(0), left(nullptr), right(nullptr) {}
};
// 构建线段树
Node* buildSegmentTree(vector<int>& nums, int start, int end) {
if (start > end) {
return nullptr;
}
Node* root = new Node(start, end);
if (start == end) {
root->sum = nums[start];
} else {
int mid = start + (end - start) / 2;
root->left = buildSegmentTree(nums, start, mid);
root->right = buildSegmentTree(nums, mid + 1, end);
root->sum = root->left->sum + root->right->sum;
}
return root;
}
// 单点修改
void update(Node* root, int index, int diff) {
if (root == nullptr) {
return;
}
if (index < root->start || index > root->end) {
return;
}
root->sum += diff;
if (root->start != root->end) {
update(root->left, index, diff);
update(root->right, index, diff);
}
}
// 区间查询
int query(Node* root, int start, int end) {
if (root == nullptr || start > root->end || end < root->start) {
return 0;
}
if (start <= root->start && end >= root->end) {
return root->sum;
}
return query(root->left, start, end) + query(root->right, start, end);
}
int main() {
vector<int> nums = {1, 3, 5, 7, 9, 11};
int n = nums.size();
Node* root = buildSegmentTree(nums, 0, n - 1);
// 区间查询 [2, 4]
cout << "Sum of range [2, 4]: " << query(root, 2, 4) << endl;
// 将索引 3 处的元素加 2
update(root, 3, 2);
// 再次区间查询 [2, 4]
cout << "Sum of range [2, 4]: " << query(root, 2, 4) << endl;
return 0;
}
这段代码中定义了一个结构体表示线段树的节点。函数用于构建线段树,它会递归地将数组的区间划分为更小的区间,直到区间只包含一个元素。函数用于单点修改,它会在给定的索引位置修改节点的值,并递归地更新受影响的节点的和。函数用于区间查询,它会递归地查找给定区间的和。
在`main`函数中,我们首先创建了一个数组,然后调用函数构建线段树。接着进行一次区间查询,并输出查询结果。然后对索引3处的元素进行了修改,并再次进行一次区间查询,输出查询结果。
注意:这段代码中假设线段树的大小是固定的,即数组的大小在构建线段树之前已经确定。如需支持动态大小的线段树,请在构建线段树时动态分配内存,并在单点修改和区间查询时更新线段树的大小。