Segment tree | Efficient implementation

29 Mar, 2022
Let us consider the following problem to understand Segment Trees without recursion.We have an array arr[0 . . . n-1]. We should be able to,  
Find the sum of elements from index l to r where 0 <= l <= r <= n-1Change the value of a specified element of the array to a new value x. We need to do arr[i] = x where 0 <= i <= n-1. 
Find the sum of elements from index l to r where 0 <= l <= r <= n-1
Change the value of a specified element of the array to a new value x. We need to do arr[i] = x where 0 <= i <= n-1. 
A simple solution is to run a loop from l to r and calculate the sum of elements in the given range. To update a value, simply do arr[i] = x. The first operation takes O(n) time and the second operation takes O(1) time.
Another solution is to create another array and store the sum from start to i at the ith index in this array. The sum of a given range can now be calculated in O(1) time, but the update operation takes O(n) time now. This works well if the number of query operations is large and there are very few updates.What if the number of queries and updates are equal? Can we perform both the operations in O(log n) time once given the array? We can use a Segment Tree to do both operations in O(Logn) time. We have discussed the complete implementation of segment trees in our previous post. In this post, we will discuss the easier and yet efficient implementation of segment trees than in the previous post.Consider the array and segment tree as shown below:  
You can see from the above image that the original array is at the bottom and is 0-indexed with 16 elements. The tree contains a total of 31 nodes where the leaf nodes or the elements of the original array start from node 16. So, we can easily construct a segment tree for this array using a 2*N sized array where N is the number of elements in the original array. The leaf nodes will start from index N in this array and will go up to index (2*N – 1). Therefore, the element at index i in the original array will be at index (i + N) in the segment tree array. Now to calculate the parents, we will start from the index (N – 1) and move upward. For index i , the left child will be at (2 * i) and the right child will be at (2*i + 1) index. So the values at nodes at (2 * i) and (2*i + 1) are combined at i-th node to construct the tree. As you can see in the above figure, we can query in this tree in an interval [L,R) with left index(L) included and right (R) excluded.We will implement all of these multiplication and addition operations using bitwise operators.Let us have a look at the complete implementation:  
C++
Java
Python3
C#
Javascript
#include <bits/stdc++.h>using namespace std; // limit for array sizeconst int N = 100000; int n; // array size // Max size of treeint tree[2 * N]; // function to build the treevoid build( int arr[]){    // insert leaf nodes in tree    for (int i=0; i<n; i++)           tree[n+i] = arr[i];         // build the tree by calculating parents    for (int i = n - 1; i > 0; --i)            tree[i] = tree[i<<1] + tree[i<<1 | 1];   } // function to update a tree nodevoid updateTreeNode(int p, int value){    // set value at position p    tree[p+n] = value;    p = p+n;         // move upward and update parents    for (int i=p; i > 1; i >>= 1)        tree[i>>1] = tree[i] + tree[i^1];} // function to get sum on interval [l, r)int query(int l, int r){    int res = 0;         // loop to find the sum in the range    for (l += n, r += n; l < r; l >>= 1, r >>= 1)    {        if (l&1)            res += tree[l++];             if (r&1)            res += tree[--r];    }         return res;} // driver program to test the above functionint main(){    int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};     // n is global    n = sizeof(a)/sizeof(a[0]);         // build tree    build(a);         // print the sum in range(1,2) index-based    cout << query(1, 3)<<endl;         // modify element at 2nd index    updateTreeNode(2, 1);         // print the sum in range(1,2) index-based    cout << query(1, 3)<<endl;     return 0;}
import java.io.*; public class GFG {         // limit for array size    static int N = 100000;         static int n; // array size         // Max size of tree    static int []tree = new int[2 * N];         // function to build the tree    static void build( int []arr)    {                 // insert leaf nodes in tree        for (int i = 0; i < n; i++)            tree[n + i] = arr[i];                 // build the tree by calculating        // parents        for (int i = n - 1; i > 0; --i)            tree[i] = tree[i << 1] +                      tree[i << 1 | 1];    }         // function to update a tree node    static void updateTreeNode(int p, int value)    {                 // set value at position p        tree[p + n] = value;        p = p + n;                 // move upward and update parents        for (int i = p; i > 1; i >>= 1)            tree[i >> 1] = tree[i] + tree[i^1];    }         // function to get sum on    // interval [l, r)    static int query(int l, int r)    {        int res = 0;                 // loop to find the sum in the range        for (l += n, r += n; l < r;                             l >>= 1, r >>= 1)        {            if ((l & 1) > 0)                res += tree[l++];                     if ((r & 1) > 0)                res += tree[--r];        }                 return res;    }         // driver program to test the    // above function    static public void main (String[] args)    {        int []a = {1, 2, 3, 4, 5, 6, 7, 8,                                9, 10, 11, 12};             // n is global        n = a.length;                 // build tree        build(a);                 // print the sum in range(1,2)        // index-based        System.out.println(query(1, 3));                 // modify element at 2nd index        updateTreeNode(2, 1);                 // print the sum in range(1,2)        // index-based        System.out.println(query(1, 3));    }} // This code is contributed by vt_m.
# Python3 Code Addition # limit for array sizeN = 100000; # Max size of treetree = [0] * (2 * N); # function to build the treedef build(arr) :     # insert leaf nodes in tree    for i in range(n) :        tree[n + i] = arr[i];         # build the tree by calculating parents    for i in range(n - 1, 0, -1) :        tree[i] = tree[i << 1] + tree[i << 1 | 1]; # function to update a tree nodedef updateTreeNode(p, value) :         # set value at position p    tree[p + n] = value;    p = p + n;         # move upward and update parents    i = p;         while i > 1 :                 tree[i >> 1] = tree[i] + tree[i ^ 1];        i >>= 1; # function to get sum on interval [l, r)def query(l, r) :     res = 0;         # loop to find the sum in the range    l += n;    r += n;         while l < r :             if (l & 1) :            res += tree[l];            l += 1             if (r & 1) :            r -= 1;            res += tree[r];                     l >>= 1;        r >>= 1         return res; # Driver Codeif __name__ == "__main__" :     a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];     # n is global    n = len(a);         # build tree    build(a);         # print the sum in range(1,2) index-based    print(query(1, 3));         # modify element at 2nd index    updateTreeNode(2, 1);         # print the sum in range(1,2) index-based    print(query(1, 3));     # This code is contributed by AnkitRai01
using System; public class GFG {         // limit for array size    static int N = 100000;         static int n; // array size         // Max size of tree    static int []tree = new int[2 * N];         // function to build the tree    static void build( int []arr)    {                 // insert leaf nodes in tree        for (int i = 0; i < n; i++)            tree[n + i] = arr[i];                 // build the tree by calculating        // parents        for (int i = n - 1; i > 0; --i)            tree[i] = tree[i << 1] +                       tree[i << 1 | 1];    }         // function to update a tree node    static void updateTreeNode(int p, int value)    {        // set value at position p        tree[p + n] = value;        p = p + n;                 // move upward and update parents        for (int i = p; i > 1; i >>= 1)            tree[i >> 1] = tree[i] + tree[i^1];    }         // function to get sum on    // interval [l, r)    static int query(int l, int r)    {        int res = 0;                 // loop to find the sum in the range        for (l += n, r += n; l < r;                             l >>= 1, r >>= 1)        {            if ((l & 1) > 0)                res += tree[l++];                     if ((r & 1) > 0)                res += tree[--r];        }                 return res;    }         // driver program to test the    // above function    static public void Main ()    {        int []a = {1, 2, 3, 4, 5, 6, 7, 8,                            9, 10, 11, 12};             // n is global        n = a.Length;                 // build tree        build(a);                 // print the sum in range(1,2)        // index-based        Console.WriteLine(query(1, 3));                 // modify element at 2nd index        updateTreeNode(2, 1);                 // print the sum in range(1,2)        // index-based        Console.WriteLine(query(1, 3));    }} // This code is contributed by vt_m.
<script>    // limit for array size    let N = 100000;           let n; // array size           // Max size of tree    let tree = new Array(2 * N);    tree.fill(0);           // function to build the tree    function build(arr)    {                   // insert leaf nodes in tree        for (let i = 0; i < n; i++)            tree[n + i] = arr[i];                   // build the tree by calculating        // parents        for (let i = n - 1; i > 0; --i)            tree[i] = tree[i << 1] +                       tree[i << 1 | 1];    }           // function to update a tree node    function updateTreeNode(p, value)    {        // set value at position p        tree[p + n] = value;        p = p + n;                   // move upward and update parents        for (let i = p; i > 1; i >>= 1)            tree[i >> 1] = tree[i] + tree[i^1];    }           // function to get sum on    // interval [l, r)    function query(l, r)    {        let res = 0;                   // loop to find the sum in the range        for (l += n, r += n; l < r;                             l >>= 1, r >>= 1)        {            if ((l & 1) > 0)                res += tree[l++];                       if ((r & 1) > 0)                res += tree[--r];        }                   return res;    }         let a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];           // n is global    n = a.length;     // build tree    build(a);     // print the sum in range(1,2)    // index-based    document.write(query(1, 3) + "</br>");     // modify element at 2nd index    updateTreeNode(2, 1);     // print the sum in range(1,2)    // index-based    document.write(query(1, 3));     </script>
Output: 
5
3
Yes! That is all. The complete implementation of the segment tree includes the query and update functions in a lower number of lines of code than the previous recursive one. Let us now understand how each of the functions works:  
1. The picture makes it clear that the leaf nodes are stored at i+n, so we can clearly insert all leaf nodes directly.
2. The next step is to build the tree and it takes O(n) time. The parent always has its less index than its children, so we just process all the nodes in decreasing order, calculating the value of the parent node. If the code inside the build function to calculate parents seems confusing, then you can see this code. It is equivalent to that inside the build function. 
tree[i]=tree[2*i]+tree[2*i+1]
 
3. Updating a value at any position is also simple and the time taken will be proportional to the height of the tree. We only update values in the parents of the given node which is being changed. So to get the parent, we just go up to the parent node, which is p/2 or p>>1, for node p. p^1 turns (2*i) to (2*i + 1) and vice versa to get the second child of p.
4. Computing the sum also works in O(log(n)) time. If we work through an interval of [3,11), we need to calculate only for nodes 19,26,12, and 5 in that order.
The idea behind the query function is whether we should include an element in the sum or whether we should include its parent. Let’s look at the image once again for proper understanding. Consider that L is the left border of an interval and R is the right border of the interval [L,R). It is clear from the image that if L is odd, then it means that it is the right child of its parent and our interval includes only L and not the parent. So we will simply include this node to sum and move to the parent of its next node by doing L = (L+1)/2. Now, if L is even, then it is the left child of its parent and the interval includes its parent also unless the right borders interfere. Similar conditions are applied to the right border also for faster computation. We will stop this iteration once the left and right borders meet.The theoretical time complexities of both previous implementation and this implementation is the same, but practically, it is found to be much more efficient as there are no recursive calls. We simply iterate over the elements that we need. Also, this is very easy to implement.
Time Complexities:
Tree Construction: O( n )
Query in Range: O( Log n )
Updating an element: O( Log n ).
Auxiliary Space: O(2*N)This article is contributed by Striver. If you like GeeksforGeeks and would like to contribute, you can also write an article using write.geeksforgeeks.org or mail your article to review-team@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above. 
vt_m
ankthon
suresh07
rishavnitro
Segment-Tree
Advanced Data Structure
Segment-Tree
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
AVL Tree | Set 1 (Insertion)
Trie | (Insert and Search)
LRU Cache Implementation
Introduction of B-Tree
Red-Black Tree | Set 1 (Introduction)
Agents in Artificial Intelligence
Decision Tree Introduction with example
Binary Indexed Tree or Fenwick Tree
Count of strings whose prefix match with the given string to a given length k
Ordered Set and GNU C++ PBDS