A Segment Tree is a data structure that is used for various range query problems, particularly in competitive programming. It allows for efficient query operations on arrays, such as finding the minimum, maximum, or sum of elements in a given range. In this article, we will learn how to implement a Segment Tree in C++ and its basic operations.
A Segment Tree is a binary tree data structure that stores information about intervals or segments. It allows for efficient query and update operations on arrays. Each node in the tree represents a segment of the array, with the root representing the entire array and leaf nodes representing individual elements.
The following diagram represents the structure of the segment tree in C++:
 Properties of Segment Trees- The root of the tree represents the whole array A[0:n-1].
- Each leaf in the Segment Tree represents a single element of the array.
- The internal nodes represent the merge of their children nodes.
- The tree is a full binary tree, with n leaves and n-1 internal nodes.
- The height of the tree is ⌈log₂n⌉.
Representation of Segment Tree in C++A Segment Tree can be implemented using an array. For an input array of size n, the Segment Tree will require 4n space in the worst case.
To represent a Segment Tree in C++, we will create a class SegmentTree that contains a std::vector to store the tree, and member functions to provide basic functionality. To keep the Segment Tree generic, we will use templates so that it can store multiple data types.
template <typename T>
class SegmentTree {
private:
vector<T> tree;
vector<T> arr;
int n;
} Here,
- tree: stores the Segment Tree.
- arr: stores the original array of elements that will be used to create the segment tree.
- n: size of the original array.
Basic Operations of Segment Tree in C++Following are some of the basic operations of Segment Tree that are required to manipulate its elements:
Operation
| Description
| Time Complexity
| Space Complexity
|
---|
Build
| Constructs the Segment Tree from the input array
| O(n)
| O(n)
|
---|
Update
| Updates a single element in the Segment Tree
| O(log n)
| O(1)
|
---|
Query
| Performs a range query on the Segment Tree
| O(log n)
| O(1)
|
---|
Print
| Prints the Segment Tree in a readable format
| O(n)
| O(n)
|
---|
Implementation of Build Function- Start with the root node representing the entire array.
- Recursively divide the array into two halves.
- Build the left and right subtrees.
- Combine the results of the left and right subtrees to build the current node.
Implementation of Update Function- Start from the root of the Segment Tree.
- If the current node’s range includes the index to be updated:
- If it’s a leaf node, update the value.
- Otherwise, recursively update the appropriate child (left or right).
- After returning from recursive calls, recalculate the current node’s value.
Implementation of Query Function- If the current node’s range is completely inside the query range, return the node’s value.
- If the current node’s range is completely outside the query range, return the identity element.
- Otherwise, split the query into two parts and recursively query the left and right children.
- Combine the results from the left and right queries and return.
Implementation of Print Function- If the current node’s range is a single element (start == end), print the node’s value along with its range.
- Otherwise, print the current node’s value and range, then recursively print the left and right children.
- Use indentation to show the tree structure.
C++ Program to Implement Segment TreeThe following program demonstrates the implementation of a Segment Tree in C++:
C++
// C++ Program to Implement Segment Tree for Minimum Range
// Queries
#include <climits>
#include <iostream>
#include <vector>
using namespace std;
// Template class for Segment Tree
template <typename T> class SegmentTree {
private:
// Segment tree to store the minimums
vector<T> tree;
// Input array
vector<T> arr;
// Size of the input array
int n;
// Helper function to get the left child of a node
int left(int node) { return 2 * node + 1; }
// Helper function to get the right child of a node
int right(int node) { return 2 * node + 2; }
// Helper function to calculate the middle index
int mid(int l, int r) { return l + (r - l) / 2; }
// Function to build the segment tree
void build(int node, int start, int end)
{
// If the current node represents a single element,
// store it in the tree
if (start == end) {
tree[node] = arr[start];
return;
}
// Calculate the middle index
int m = mid(start, end);
// Recursively build the left and right children
build(left(node), start, m);
build(right(node), m + 1, end);
// Internal node will store the minimum of the two
// children
tree[node]
= min(tree[left(node)], tree[right(node)]);
}
// Function to update the value at a specific index in
// the segment tree
void update(int node, int start, int end, int idx,
T val)
{
// If the current node represents a single element,
// update it
if (start == end) {
arr[idx] = val;
tree[node] = val;
return;
}
// Calculate the middle index
int m = mid(start, end);
// Recursively update the left or right child
if (idx <= m)
update(left(node), start, m, idx, val);
else
update(right(node), m + 1, end, idx, val);
// Internal node will store the minimum of the two
// children
tree[node]
= min(tree[left(node)], tree[right(node)]);
}
// Function to query the minimum value in a given range
T query(int node, int start, int end, int l, int r)
{
// If the current node's range is completely outside
// the query range
if (r < start || end < l)
return INT_MAX;
// If the current node's range is completely inside
// the query range
if (l <= start && end <= r)
return tree[node];
// Calculate the middle index
int m = mid(start, end);
// Recursively query the left and right children and
// combine the results
T left_min = query(left(node), start, m, l, r);
T right_min = query(right(node), m + 1, end, l, r);
return min(left_min, right_min);
}
public:
// Constructor to initialize the segment tree with the
// input array
SegmentTree(const vector<T>& a)
: arr(a)
, n(a.size())
{
// Resize the tree to accommodate the segment tree
// nodes
tree.resize(4 * n);
// Build the segment tree
build(0, 0, n - 1);
}
// Public function to update the value at a specific
// index
void update(int idx, T val)
{
update(0, 0, n - 1, idx, val);
}
// Public function to query the minimum value in a given
// range
T query(int l, int r)
{
return query(0, 0, n - 1, l, r);
}
};
int main()
{
// Input array
vector<int> arr = { 1, 3, 2, 7, 9, 11 };
// Create the Segment Tree object
SegmentTree<int> st(arr);
// Display initial information
cout << "Segment Tree for Minimum Range Queries:"
<< endl;
cout << "Original Array: ";
for (int num : arr)
cout << num << " ";
cout << endl;
// Perform some range queries
cout << "Minimum in range [1, 4]: " << st.query(1, 4)
<< endl;
cout << "Minimum in range [0, 5]: " << st.query(0, 5)
<< endl;
// Update an element in the array and perform queries
// again
st.update(2, 1);
cout << "After updating index 2 to 1:" << endl;
cout << "Minimum in range [1, 4]: " << st.query(1, 4)
<< endl;
cout << "Minimum in range [0, 5]: " << st.query(0, 5)
<< endl;
return 0;
}
Output
Segment Tree for Minimum Range Queries:
Original Array: 1 3 2 7 9 11
Minimum in range [1, 4]: 2
Minimum in range [0, 5]: 1
After updating index 2 to 1:
Minimum in range [1, 4]: 1
Minimum in range [0, 5]: 1 Applications of Segment TreeFollowing are some of the common applications of the segment tree:
- Segment trees are commonly used for finding the minimum, maximum, sum, or product of elements in a given range.
- Used for implementing range queries in database management systems.
- Used for performing efficient operations on image segments.
- Used for monitoring network traffic and performance metrics over time intervals.
- Used for implementing efficient collision detection in 2D games.
|