Segment tree is a data structure that supports queries and update on intervals.
It can be used for solving range query problems like finding minimum, maximum, sum, greatest common divisor, least common denominator in array in logarithmic time.
Example range sum query problem:
given an array, you need to do the following operations:
add k to every numbers in a index range.
get the sum of all the numbers within a index range.
The merge operation can be sum or any operation that satisfies associative law.
merge(merge(a, b), c) = merge(a, merge(b, c))
// Example
max(max(a, b), c) = max(a, max(b, c))
gcd(gcd(a, b), c) = gcd(a, gcd(b, c))
// OJ: https://leetcode.com/problems/range-sum-query-mutable/// Author: github.com/lzl124631x// Time: // NumArray: O(N)// update: O(1)// sumRange: O(logN)// Space: O(N)classNumArray { vector<int> tree;int N;voidbuildTree(vector<int> &nums) { for (int i = 0, j = N; i < N;) tree[j++] = nums[i++]; // the input array values are stored in `tree[N...(2 * N - 1)]`
for (int i = N - 1; i > 0; --i) tree[i] = tree[2 * i] + tree[2 * i + 1]; // from `tree[N-1]` to `tree[1]`, build the non-leaf nodes. Note that we don't use `tree[0]`.
}public:NumArray(vector<int>& nums) {if (nums.empty()) return; N =nums.size(); tree =vector<int>(N *2);buildTree(nums); // Segment tree requires initialization. }voidupdate(int i,int val) { i += N; // Offset N to get the leaf nodetree[i] = val;while (i >0) { i /=2; // Move to the parent nodetree[i] =tree[2* i] +tree[2* i +1]; // Update the parent node } }intsumRange(int i,int j) { i += N; // Offset N to get the leaf nodes j += N;int sum =0;while (i <= j) { // When the range is not emptyif (i %2) sum +=tree[i++];if (j %2==0) sum +=tree[j--]; i /=2; j /=2; }return sum; }};
Another OOD implementation:
// OJ: https://leetcode.com/problems/range-sum-query-mutable/// Author: github.com/lzl124631x// Time: // NumArray: O(N)// update: O(1)// sumRange: O(logN)// Space: O(N)typedefintMerge(constint&a,constint& b);classSegTree { vector<int> tree;int N; Merge *merge;voidbuildTree(vector<int> &nums) {for (int i =0, j = N; i < N;) tree[j++] =nums[i++];for (int i = N -1; i >0; --i) tree[i] =merge(tree[2* i],tree[2* i +1]); }public:SegTree(vector<int> &nums,Merge merge) { N =nums.size();tree.resize(N *2);this->merge = merge;buildTree(nums); }voidupdate(int i,int val) { i += N;tree[i] = val;while (i >0) { i /=2;tree[i] =merge(tree[2* i],tree[2* i +1]); } }intquery(int from,int to,int def) {int i = from + N, j = to + N;int ans = def;while (i <= j) {if (i %2) ans =merge(ans,tree[i++]);if (j %2==0) ans =merge(ans,tree[j--]); i /=2; j /=2; }return ans; }};classNumArray { SegTree* tree;public:NumArray(vector<int>& nums) {if (nums.empty()) return; tree =newSegTree(nums, [](constint&a,constint&b) { return a + b; }); }voidupdate(int i,int val) {tree->update(i, val); }intsumRange(int i,int j) {returntree->query(i, j,0); }};
Lazy Propogation
When we want to update an interval all at once, we need to use lazy propagation to ensure good run-time complexity.