Summary of Primary Segment Trees

Posted by hrosas on Thu, 22 Aug 2019 10:59:55 +0200

Line segment tree is an efficient data structure to maintain the interval. It divides the interval into two parts by the characteristics of the tree. Through continuous division and recursion, it completes the efficient management and maintenance of the interval data.

In order to write the interval conveniently, we often take the interval of the line segment tree as the power of 2 to divide the interval conveniently and form a perfect binary tree.

  

 

Segment trees often have several operations, including

1. Initialization of the segment tree; (Pay attention to the size determination in the initialization process, determine the size of the segment tree)

2. Modify the value of a node; (In the process of modification, the interval needs to be maintained continuously)

3. Find the minimum value of an interval. (This is to maintain the minimum segment tree, of course, recursive, you can also maintain the maximum!)

 

 

On the implementation of the above three operations:

/*
    Here we set up a line segment tree to maintain the minimum value. Is it maintained in other words, just need to change some initialization and update the details of the node.
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <string>
#include <cctype>
#include <algorithm>
#include <vector>
#include <queue>
#include <list>
#include <map>
#include <stack>
#include <set>
using namespace std;

const int INF = 1e8;            // Because it is a segment tree that maintains the minimum value, we initialize the maximum value, of course.
const int MAX = 1 << 17;        // segment tree There are at most so many nodes.
int date[MAX*2 - 1];            //  But it has to be doubled and doubled, because it still has something on it. node
int n;                          //Global variables are used to represent the size of line segment trees.
//Initialization
void Initial(int n_){           //n_ It's the size of the data you need.
    n = 1;
    while(n < n_)   n <<= 1;       // For convenience, the bottom size of the segment tree is set to a power of 2. n_Expanded to n !
    printf("N : %d \n", n);
    for(int i = 0; i < 2 * n - 1; i++)  date[i] = INF;
}
//Indexing Subscripts k The element is updated to a; There K Index subscripts are data entered from 0 by leaf points.
void Update(int k, int a){
    k += n - 1;                 //Locate the leaf node directly and update it.
    date[k] = a;
    while(k > 0){
        k = (k - 1) / 2;        //Locate parent node
        date[k] = min(date[2*k+1], date[2*k+2]);
    }
}
// What we need is the interval. [a , b) Maintenance value in it, [ l ,r )Like the result of his interval;
// k Is the node number, of course [l, r) It is the interval corresponding to this node.
// Attention, a , b The interval is not the one above the line segment tree; it's the interval where you enter data.
int Query(int a, int b, int k, int l, int r){
    if(r <= a||l >= b)  return INF;     //There is no intersection of intervals at all.
    if(a <= l&&b >= r)  return date[k];
    else{   //That's where only part of the intersection is present.
        int x = Query(a, b, 2 * k + 1, l, (l + r) / 2); //Left node
        int y = Query(a, b, 2 * k + 2, (l + r) / 2, r); //Right node
        return min(x, y);
    }
}
int main()
{
    printf("THE START:\n");
    Initial(16);
    printf("THE INITIAL IF FINISHED\n");
    for(int i = 0; i < 16; i++){
        Update(i, 16 - i);
    }
    for(int i = 0; i < 16; i++){
        printf("%d ", 16 - i);
    }
    cout<<endl;
    printf("%d\n", Query(0, 16, 0, 0, 16));
    printf("%d\n", Query(0, 10, 0, 0, 16));
    printf("%d\n", Query(12, 16, 0, 0, 16));
    return 0;
}

 

 

Notes in operation:

1. With us we are 2n elements, but our segment tree interval requires 2n+1-1; this is because our basic elements are in leaves, and they need the parent node to record values. The parent node is the maintained data!

2. Note that our arrays start from zero, unlike from 1, their parent nodes and left and right sons have slightly different relationships in the table below.

EG: Start from zero: Parent: X - > LeftSon: X / 2 + 1; RightSon: x / 2 + 2;

From the beginning: Parent: X - > LeftSon: X / 2 + 0; RightSon: x / 2 + 1;

3. The update operation is based on the operation of leaf nodes, because the location of leaf nodes should be located directly.

4. When we operate the interval, we can deduce the interval from the subscript, but it is too cumbersome (representing only the personal point of view) to put the range of the interval directly into the parameters of the function. Note that Query(a, b, 0, 0, n) is required for external calls.

5. Be careful when initializing, he is n <== 1; don't write n > 1; this sub-son's words are dead cycle!

Topics: C++