Segment Tree | Sum of a given range


Segment Tree

A segment tree is a tree data structure used for storing intervals and segments. It is a static structure, i.e. it cannot be modified once it is built. Segment trees are used to handle range queries on an array or a similar linear data structure.

In a segment tree, we divide an input array into segments and precompute the values for these segments. Each node in a segment tree depicts an interval or segment of the array. The root node represents the entire array and each child node represents the segments formed by dividing the parent node. This division leads to representing the leaf nodes as individual elements of the array.

Segment Tree for Range of Sum queries −

Original Array: [7, 1, 2, 3, 6, 5, 0, 4]
Segment Tree:
         38
       /    \
      13     25
     / \     / \
    8   5   11   4
   / \ / \  / \ / \
  7  1 2  3 6  5 0 4

Segment Tree has a complexity of O(logN) for each range query and update operation.

Range Sum Query − RSQ is a common problem where for a given array, we need to find the sum of all elements in a range specified. Segment Tree is the most efficient data structure used for the problem.

Problem Statement

Given an array of integers arr[] containing N elements along with start and end index. The task is to find the sum of all the elements in the range [start, end].

Sample Example 1

Input

N = 8
arr[] = {1, 7, 8, 9, 5, 2, 3, 4}
start = 2
end = 6

Output

27

Explanation

Array within the specified range is: {8, 9, 5, 2, 3}
In the given range, the sum of elements = 8 + 9 + 5 + 2 + 3 = 27.

Sample Example 2

Input

N = 3
arr[] = {1, 3, 2}
start = 1
end = 1

Output

3

Explanation

Array within the specified range is: {3}
In the given range, the sum of elements = 3.

Solution Approach

The Range Sum Query problem can be solved by following the following steps −

  • Construct a segment tree for the input array.

  • Recursively find the segment of the tree contained in the query. Each segment can be categorised as one of the following −

    • Complete Overlap − Current segment is completely within the query range.

    • No Overlap − Current segment is completely outside the query range.

    • Partial Overlap − Current segment partially covers the query range.

Pseudocode to Construct Segment Tree

function segmentTreeUtil(arr, seg_start, seg_end, tree, curr):
   if seg_start == seg_end:
      tree[curr] = arr[seg_start]
      return arr[seg_start]
    
   mid = getMid(seg_start, seg_end)
   tree[curr] = segmentTreeUtil(arr, seg_start, mid, tree, curr * 2 + 1) + segmentTreeUtil(arr, mid + 1, seg_end, tree, curr * 2 + 2)
   return tree[curr]

function segmentTree(arr, n):
   x = ceil(log2(n))
   max_size = 2 * pow(2, x) - 1
   tree = new int[max_size]
   segmentTreeUtil(arr, 0, n - 1, tree, 0)
   return tree

Pseudocode for RSQ

function RSQUtil(tree, seg_start, seg_end, start, end, curr):
   if start <= seg_start AND end >= seg_end:
      return tree[curr]   
   if seg_end < start OR seg_start > end:
      return 0    
   mid = getMid(seg_start, seg_end)
   return RSQUtil(tree, seg_start, mid, start, end, 2 * curr + 1) + RSQUtil(tree, mid + 1, seg_end, start, end, 2 * curr + 2)
function RSQ(tree, n, start, end):
   if start < 0 OR end > n - 1 OR start > end:
      print "Query Range Invalid"
      return -1
   return RSQUtil(tree, 0, n - 1, start, end, 0)

Example: C++ Implementation

The following code constructs a segment tree to solve the RSQ problem.

#include <bits/stdc++.h>
using namespace std;

int getMid(int x, int y){
   return x + (y - x) / 2;
}
// Recursive function used to find the sum of elements in the query range
int RSQUtil(int *tree, int seg_start, int seg_end, int start, int end, int curr){
   // Complete Overlap
   if (start <= seg_start && end >= seg_end)
      return tree[curr];
   // No Overlap
   if (seg_end < start || seg_start > end)
      return 0;
   // Partial Overlap
   int mid = getMid(seg_start, seg_end);
   return RSQUtil(tree, seg_start, mid, start, end, 2 * curr + 1) + RSQUtil(tree, mid + 1, seg_end, start, end, 2 * curr + 2);
}
// Calculates RSQ by calling RSQUtil(
int RSQ(int *tree, int n, int start, int end){
   if (start < 0 || end > n - 1 || start > end){
      cout << "Query Range Invalid";
      return -1;
   }
   return RSQUtil(tree, 0, n - 1, start, end, 0);
}
// Creates Segment Tree for input array
int segmentTreeUtil(int arr[], int seg_start, int seg_end, int *tree, int curr){
   // Base Case of only one element in array
   if (seg_start == seg_end){
      tree[curr] = arr[seg_start];
      return arr[seg_start];
   }
   // Dividing array into segments
   int mid = getMid(seg_start, seg_end);
   tree[curr] = segmentTreeUtil(arr, seg_start, mid, tree, curr * 2 + 1) + segmentTreeUtil(arr, mid + 1, seg_end, tree, curr * 2 + 2);
   return tree[curr];
}
// Creates Segment Tree by allocating memmory and calling segmentTreeUtil(
int *segmentTree(int arr[], int n){
   int x = (int)(ceil(log2(n)));
   int max_size = 2 * (int)pow(2, x) - 1;
   int *tree = new int[max_size];
   segmentTreeUtil(arr, 0, n - 1, tree, 0);
   return tree;
}
int main(){
   int arr[] = {1, 7, 8, 9, 5, 2, 3, 4};
   int n = 8;
   int *tree = segmentTree(arr, n);
   int start = 2;
   int end = 6;
   cout << "Sum = " << RSQ(tree, n, start, end) << endl;
   return 0;
}

Output

Sum = 27

Time Complexity − The time complexity for building the tree is O(N) and the time complexity for each RSQ is O(logn). Thus, for Q queries, the time complexity is O(Q*logN).

Space Complexity − O(N)

Conclusion

In conclusion, the Range Sum Query (RSQ) using the Segment Tree is an efficient data structure and algorithm for finding the minimum element in a given range of an array. The time complexity of each query is O(logN) which is better than the naive approach to iterate over the array having a complexity of O(N).

Updated on: 03-Nov-2023

243 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements