Tree array and line segment tree

Source: Internet
Author: User

Tree array and line segment tree

I. Tree Array

A tree array is a useful data structure for changing elements and summation of an array. Both operations are O (logn ).

Requirements:Sometimes we need to frequently calculate the first k items of the array and the sum of the arrays from small I to j, so that the time complexity in each worst case will be O (N ), this is too inefficient. The tree array is mainly used to solve such a problem. Tree Arrays can be summed and modified within O (lgN) time.

The tree array requires an additional array. We set it to C [N], and the original array to A [N]. each element C [I] indicates the sum of A [I-2 ^ k + 1] to A [I], where k is the number of 0 at the end of I in binary. Note that bitwise operations can be used to evaluate 2 ^ k. In this way, we can get:

Similar to a tree, it is called a tree array.

Code:

(1) Evaluate the k power of 2:

 

// Get the last 2 ^ k of I, k is the number of I at the end of the binary 0 int lowBit (int I) {return I & (I ^ (I-1 )); // return k &-k ;}

 

(2) create a tree Array

 

// Obtain the array Cvoid getCArray (int * a, int n) {for (int I = 1; I <= n; I ++) {int k = lowBit (I ); C [I] = 0; for (int j = I-k + 1; j <= I; j ++) {C [I] + = a [j] ;}}

 

(3) Calculate the sum of the First n items

 

// Calculate the first I and int sum (int I) {int s = 0; while (I> = 1) {s + = C [I]; I = I-lowBit (I);} return s ;}

 

(4) Update the tree Array

 

// I indicates the small I value, value is the value to be added, and n is the array length void update (int I, int value, int n) {while (I <= n) {C [I] = C [I] + value; // change the subscript I value I = I + lowBit (I );}}

 

Complete code:

 

# Include
 
  
Using namespace std; # define N 10int C [N]; // array C [I] is a [I-2 ^ k + 1] to a [I] and where k is the number of 0 at the end of the binary I // get the last 2 ^ k ,, k is the number of I at the end of the binary 0 int lowBit (int I) {return I & (I ^ (I-1); // return k &-k ;} // obtain the array Cvoid getCArray (int * a, int n) {for (int I = 1; I <= n; I ++) {int k = lowBit (I ); C [I] = 0; for (int j = I-k + 1; j <= I; j ++) {C [I] + = a [j] ;}}// evaluate the I items before the array and int sum (int I) {int s = 0; while (I> = 1) {s + = C [I]; I = I-lowBit (I);} return s ;}// I represents the small I value, value is the value to be added, and n is the array length void update (int I, int value, int n) {while (I <= n) {C [I] = C [I] + value; // change the subscript I value I = I + lowBit (I) ;}} int main () {int n; cin> n; int * a = new int [n + 1]; for (int I = 1; I <= n; I ++) cin> a [I]; getCArray (a, n); // obtain the array Ccout <sum (5) <endl; update (3, 2, 5 ); // Add a [3] to 2 cout <sum (5) <endl; cout <sum (4) <endl; delete [] a; return 0 ;}
 

 

Ii. Line Segment tree

The line segment tree is mainly used to find the minimum, maximum, or sum of a certain range, which can be solved within the O (lgN) time, similar to the Interval Tree. Tree arrays are similar to line tree, but the problems solved by tree Arrays can be basically solved by line tree, while tree arrays that line tree trees can solve may not be able to solve. In comparison, tree arrays are much more efficient. Tree arrays are sometimes hard to understand. For example, you can use a tree array to calculate the number of reverse orders. Therefore, you are advised to use a tree array. I don't need to use a tree array.

(1) concept:

Each node in the line segment tree represents an interval, and the child node represents the left and right half of the parent node, for example, the father's interval is [a, B]. then (c = (a + B)/2) the left son's interval is [a, c], and the right son's interval is [c + 1, B].

 

(2) understanding a line segment tree from an example

The following describes the line segment tree from a classic example. The problem is as follows: from the array arr [0... n-1] To find the minimum value of an array within a certain range. The array size is fixed, but the values of elements in the array can be updated at any time.

A simple solution to this problem is to traverse the array range to find the minimum value. The time complexity is O (n), and the extra space complexity is O (1 ). When the data volume is large and query operations are frequent, the time consumed may not meet the requirements.

Another solution is to use a two-dimensional array to save the minimum value in the previously calculated range [I, j], so the pre-processing time is O (n ^ 2 ), the query time is O (1), but an additional O (n ^ 2) space is required. When the data volume is large, the consumption of this space is huge, it is also troublesome to update the minimum value of a two-dimensional array when a value in the array is changed.

We can use the line segment tree to solve this problem: the preprocessing time is O (n), the query and update operations O (logn), and the extra space O (n) is required ). Based on this problem, we construct the following Binary Tree

· Leaf nodes are elements in the original arr group.

· A non-leaf node represents the minimum value between the leaf nodes of all its children and grandchildren.

For example, for an array [2, 5, 1, 4, 9, 3], the following binary tree can be constructed (the background is white to represent the leaf node, the value of a non-leaf node is the minimum value in its corresponding array range. For example, the root node represents the array range arr [0... the minimum value within 5] is 1 ):

Image 3

Because the parent node interval of a line segment tree is evenly divided into left and right Subtrees, the line segment tree is a Complete Binary Tree,For a Complete Binary Tree Containing n leaf nodes, it must have n-1 Non-leaf nodes with a total of 2-1 nodes.Therefore, the space complexity required for storing line segments is O (n ). So how does a line segment tree operate )?

 

(3.1) create a line segment tree

For line tree, we can choose the same chain structure as ordinary binary tree. Because the line segment tree is a complete binary tree, we can also store it using arrays. The discussion and code below are all arrays to store the line segment tree. The node structure is as follows (note that when the array is used for storage, the valid space is 2n-1, and the actual space is more than that. For example, although the leaf node 1 and 3 in the above line segment tree do not have left and right Subtrees, they actually occupy array space, the actual space is the number of nodes full of Binary Trees: 2 ^ (ceil (logN) + 1)-1. logN is the height of the tree, but the space complexity is also O (n ).

 

structSegTreeNode{  int val;};

 

Defines the segment tree SegTreeNode segTree [n] that contains n nodes, and segTree [0] indicates the root node. For node segTree [I], its left child is segTree [2 * I + 1], and the right child is segTree [2 * I + 2].

We can start from the root node, divide the interval, and recursively create a line segment tree. The function of creating a line segment tree is as follows:

 

Const int MAXNUM = 1000; struct SegTreeNode {int val;} segTree [MAXNUM]; // defines the line segment tree/* function: Build the line segment tree root: the root node subscript arr of the current line segment tree: used to construct the array istart of the Line Segment tree: the starting position of the array iend: the end position of the array */void build (int root, int arr [], int istart, int iend) {if (istart = iend) // leaf node segTree [root]. val = arr [istart]; else {int mid = (istart + iend)/2; build (root * 2 + 1, arr, istart, mid ); // recursively construct the left subtree build (root * 2 + 2, arr, mid + 1, iend); // recursively construct the right subtree // based on the values of left and right subtree nodes, update the value of the current root node segTree [root]. val = min (segTree [root * 2 + 1]. val, segTree [root * 2 + 2]. val );}}

 

(3.2) query the line segment tree

If a line segment tree has been built, how can we find the minimum value of a specific interval on it? The query idea is to select some intervals so that they can be connected to exactly cover the entire query interval. Therefore, the line segment tree is suitable for solving"Information of adjacent intervals can be merged into the same information of two intervals.. The Code is as follows:

 

/* Function: query the interval of a line segment tree root: subscript of the root node of the current line segment tree [nstart, nend]: range [qstart, qend] represented by the current node: interval */int query for this query (int root, int nstart, int nend, int qstart, int qend) {// no intersection between the query interval and the current node interval if (qstart> nend | qend <nstart) return INFINITE; // The current node interval is included in the query interval if (qstart <= nstart & qend> = nend) return segTree [root]. val; // query from left and right Subtrees respectively, and return a smaller value of int mid = (nstart + nend)/2 for the query results; return min (query (root * 2 + 1, nstart, mid, qstart, qend), query (root * 2 + 2, mid + 1, nend, qstart, qend ));}

 

Example ):

1. When we want to query the minimum value of the range [], we need to query the Left and Right Subtrees separately from the root node, when querying the left subtree, the interval [] is included in the query interval [], and the value 1 of the current node is returned. When querying the right subtree, no intersection exists between the node range [] and the query interval [], and positive infinity INFINITE is returned. The query result is 1 with a smaller value of the two subtree query results. Therefore, the result is 1.

2. When the query interval is [], the node interval [] of the Left subtree is included in the range [] starting from the root node, and 1 of the current node is returned; when querying the right subtree, continue to recursively query the left and right subtree of the right subtree. When querying non-leaf node 4, continue the recursive query: the node interval [3, 3] of leaf node 4 is included in the query interval [], and 4 is returned. The Node ranges [4, 4] and [] of leaf node 9 have no intersection, and INFINITE is returned, therefore, min (4, INFINITE) = 4 is returned for non-leaf node 4. The Node ranges [5, 5] and [0, 3] of leaf node 3 have no intersection, and INFINITE is returned, therefore, for non-leaf node 3, min (4, INFINITE) = 4 is returned, so min () = 1 is returned for the root node.

(3.3) update a Single Node

Updating a single node only updates the value of a leaf node of a line segment tree, but updating a leaf node affects the value of its parent node. Therefore, after updating a child node, update the value of its parent node.

 

/* Function: update the value of a leaf node in a line segment tree root: root Node subscript of the current line segment tree [nstart, nend]: range index represented by the current node: subscript addVal of the node to be updated in the original array arr: The updated value (the original value is added with addVal) */void updateOne (int root, int nstart, int nend, int index, int addVal) {if (nstart = nend) {if (index = nstart) // locate the corresponding node and update the segTree [root]. val + = addVal; return;} int mid = (nstart + nend)/2; if (index <= mid) // update updateOne (root * 2 + 1, nstart, mid, index, addVal) in the left subtree; else updateOne (root * 2 + 2, mid + 1, nend, index, addVal); // update the value of the current node in the right subtree // update the value of segTree [root] based on the value of the left and right subtree. val = min (segTree [root * 2 + 1]. val, segTree [root * 2 + 2]. val );}

 

For example, if we want to update leaf node 4 (addVal = 6) and the updated value is changed to 10, the value of its parent node changes from 4 to 9, and the value of non-leaf node 3 remains unchanged after update, the root node remains unchanged after being updated.

 

(3.4) Interval Update

Interval Update is used to update the values of leaf nodes in a certain interval. Because more than one leaf node is involved, the leaf node affects its non-leaf parent node, there will also be many non-leaf nodes that need to be updated for backtracking. If one-time update is complete, the time complexity of the operation will certainly not be O (lgn ), for example, if we want to update the leaf nodes in the interval [], we need to update all the other nodes except the leaf nodes 3 and 9. This introduces the concept of latency tag in the line tree, which is also the essence of the line tree.

Latency tag: a new tag is added for each node to record whether the node has made some modifications (such modifications will affect its subnodes). For any interval modifications, weDivide it into nodes in the line segment tree according to the interval query method.And then modify the information of these nodes, and mark these nodes with the mark representing the modification operation. When modifying and querying, if we reach a node p and decide to consider its subnodes, we need to check whether node p is marked,If any, modify the information of its subnodes according to the tag, and mark the subnodes with the same tag.P Mark.

Therefore, you need to add the latency tag field to the online segment tree. In this example, we add the tag and addMark to indicate that the child nodes of the node Add the addMark value based on the original value, at the same time, you also need to modify the creation function build and query function. The modified code is represented in red, and the interval update function is updated. The Code is as follows:

 

Const int INFINITE = INT_MAX; const int MAXNUM = 1000; struct SegTreeNode {int val; int addMark; // latency tag} segTree [MAXNUM]; // defines the line segment tree/* function: build a line segment tree root: root Node subscript of the current line segment tree arr: used to construct an array istart: array start position iend: array End position */void build (int root, int arr [], int istart, int iend) {segTree [root]. addMark = 0; // ---- set the tag latency record domain if (istart = iend) // The leaf node segTree [root]. val = arr [istart]; else {int mid = (istart + iend)/2; build (root * 2 + 1, Rr, istart, mid); // recursively construct the left subtree build (root * 2 + 2, arr, mid + 1, iend ); // recursively construct the right subtree // update the value of the current root node based on the value of the left and right subtree node [root]. val = min (segTree [root * 2 + 1]. val, segTree [root * 2 + 2]. val) ;}}/* function: the flag field of the current node transmits root to the child node: root Node subscript of the current line segment tree */void pushDown (int root) {if (segTree [root]. addMark! = 0) {// set the flag field of the left and right child nodes, because the child node may be marked with multiple delays and not passed down // It is a "+ =" segTree [root * 2 + 1]. addMark + = segTree [root]. addMark; segTree [root * 2 + 2]. addMark + = segTree [root]. addMark; // set the value of the child node based on the flag field. Because we calculate the minimum value of the interval, when each element // element in the interval is added with a value, the minimum value of the interval is also added with this value segTree [root * 2 + 1]. val + = segTree [root]. addMark; segTree [root * 2 + 2]. val + = segTree [root]. addMark; // after passing, the current node marks the domain to clear the segTree [root]. addMark = 0 ;}}/* function: query the interval of a line segment tree root: root Node subscript of the current line segment tree [nstart, nend]: interval represented by the current node [qstart, qend]: The query interval */int query (int root, int nstart, int nend, int qstart, int qend) {// no intersection between the query interval and the current node interval if (qstart> nend | qend <nstart) return INFINITE; // The current node interval is included in the query interval if (qstart <= nstart & qend> = nend) return segTree [root]. val; // query from left and right Subtrees respectively, return a smaller value of pushDown (root) for the query results of the two; // ---- the latency flag field transmits int mid = (nstart + nend) Down) /2; return min (query (root * 2 + 1, nstart, mid, qstart, qend), query (root * 2 + 2, mid + 1, nend, qstart, qend);}/* function: update the value of the leaf node in a certain interval of the Line Segment tree root: the root node subscript of the current line segment tree [nstart, nend]: range [ustart, uend]: interval addVal to be updated: value to be updated (the original value is added with addVal) */void update (int root, int nstart, int nend, int ustart, int uend, int addVal) {// There is no intersection between the update interval and the current node interval if (ustart> nend | uend <nstart) return; // The current node interval is included in the update interval if (ustart <= nstart & uend> = nend) {segTree [root]. addMark + = addVal; segTree [root]. val + = addVal; return;} pushDown (root); // The latency flag is passed down. // update the left and right children node int mid = (nstart + nend)/2; update (root * 2 + 1, nstart, mid, ustart, uend, addVal); update (root * 2 + 2, mid + 1, nend, ustart, uend, addVal ); // update the value of the current node based on the left and right subtree values: segTree [root]. val = min (segTree [root * 2 + 1]. val, segTree [root * 2 + 2]. val );}

 

Example of Interval Update: when we want to add 2 to the leaf node of the interval [], we use the interval query method to locate the non-leaf node [0-2] From the root node. set its value to 1 + 2 = 3 and set its latency tag to 2. The update is complete. When we want to query the minimum value within the interval, when the range [] is found, it is found that its mark is not 0, and it also needs to be searched down, so the mark should be passed down, set the value of node [0-1] to 2 + 2 = 4, the flag to 2, and the value of node [2-2] to 1 + 2 = 3, set the flag to 2 (in fact, the flag of the leaf node does not work, this is for Operation consistency), and then return the query result: [0-1] node value 4; when we update the interval [0, 1] (increase 3) again, We query the node [0-1] and find that its flag value is 2, therefore, set the label value to 2 + 3 = 5 and the node value to 4 + 3 = 7;

In fact, when the left and right values of the Interval Update are equal ([I, I]), it is equivalent to updating a single node. Updating a single node is only a special case of Interval Update.

 

The above introduction from: http://www.cnblogs.com/TenosDoIt/p/3453089.html

(3.5) Application of Line Segment tree:

(1) There is a number of columns and the initial values are all 0. You can perform one of the following three operations each time:

A. Add a specific value to each number in a specified range;

B. set all the numbers in the specified interval to a uniform value;

C. Ask the minimum, maximum, and sum of all numbers in a range.

After a series of a. B. operations are provided, the result of c is output.

[Problem Analysis]

This is a typical application of the Line Segment tree. Several variables are maintained on each node: delta (added value of the interval), same (the interval is set to a value), min (minimum value of the interval), max (maximum value of the interval ), sum (range and), where delta and same are "latency tags ".

(2) discuss a question in the range of all natural numbers not greater than 30000: We know n line segments, input endpoints to you in sequence, and then have m (≤ 30000) inquiries, for each query, enter a vertex and specify the number of lines on which the vertex appears.

[Problem Analysis]

In this issue, we can directly establish a line segment tree for the problem handling interval, and maintain the number of times the interval is overwritten on the online segment tree. Insert n line segments into the line segment tree. Then, you can directly query the number of times overwritten for each point you are asking.

(3) A train passes through C cities and the city numbers are 1 to C in sequence. There are a total of S seats on the train. The Railway Bureau stipulates that only one ticket can be sold, that is, all passengers on the bus have seats. The ticket sales system is run by a computer. Each ticket sales application contains three parameters, which are represented by O, D, and N respectively. O is the starting station, D is the destination station, N is the number of tickets, and the ticket sales system determines whether the ticket sales application is accepted or not accepted, the ticket application is accepted only when there are N or more empty seats on the train from O to D. Please write a program, implement this Automatic Ticketing System.

[Problem Analysis]

Here we can place all the stations on one number axis sequentially, establish a line segment tree on the number axis, and maintain the delta and max intervals on the online segment tree. Each time you determine whether a ticket sales application is feasible, it is the maximum value in the query interval. Each ticket sales request is inserted to add the number of votes to all elements in the same interval.

The information maintained on the online segment tree includes both bottom-to-bottom recursion and top-to-bottom transmission, which can fully train the basic operations of the Line Segment tree.

(4) give an n * n checkboard. At first, each checkboard is white. Black or white paint is required for M times. Each paint area is a rectangular area at the edge of a parallel board.

Input n, M, and the area and color of each paint. The number of squares on the checker is white after M is brushed.

[Problem Analysis]

First, let's start with a simple one-dimensional problem. That is, for a white line segment with a length of n, it is modified M times (the color of a subarea is updated each time ). Ask how long the remaining white area is.

For this problem, it is easy to think of building a line segment tree model. The complexity is O (Mlgn ).

To expand to two dimensions, we need to adjust the line segment tree. That is, we first create a line segment tree on the abscissa. Each of its nodes is a line segment tree established on the Y coordinate (that is, the tree contains a tree. Is called a two-dimensional line segment tree ). The complexity is O (M (logn) ^ 2 ).

Figure 4

The complete code of the Line Segment tree is given below:

 

Segmentation. h

 

/* Function: Frequently queries the minimum value of an array range, including updating the value of an array range or a node. Implementation: Line Segment tree, using an array to access a line segment tree with n nodes (because it is a Complete Binary Tree) requires 2 ^ lg (ceil (n) + 1)-1 space, is a balanced search tree complexity: preprocessing: creating a line segment tree O (n); query update time complexity O (lgN), requires additional storage space O (N) */# include
 
  
Using namespace std; # define maxInt 0x70000000 # define N 100 struct SegNode {int minVal; int left; int right; int addMark; SegNode (): left (0 ), right (0), minVal (0), addMark (0) {}}; class SegTree {private: SegNode sn [N]; void pushDown (int root); public: void buildSegTree (int root, int a [], int begin, int end); // create a line segment tree int queryMin (int root, const int & qstart, const int & qend, int start, int end); // query the minimum value of void updateOneNode in a certain range (int root, const int & index, const int & value, int start, int end ); // update the value of a node void updateInterval (int root, const int & ustart, const int & uend, const int & value, int start, int end ); // update the value of the interval void display (int n );};
 

 

Segmentation. cpp

 

# Include SegmentionTree. h # include
 
  
Using namespace std; void SegTree: buildSegTree (int root, int a [], int begin, int end) {if (begin = end) {sn [root]. minVal = a [begin]; sn [root]. left = begin; sn [root]. right = begin; sn [root]. addMark = 0; return;} int mid = (begin + end)/2; buildSegTree (root * 2 + 1, a, begin, mid ); buildSegTree (root * 2 + 2, a, mid + 1, end); sn [root]. minVal = min (sn [root * 2 + 1]. minVal, sn [root * 2 + 2]. minVal); sn [root]. addMark = 0; sn [root]. Left = begin; sn [root]. right = end; return;} void SegTree: pushDown (int root) {if (sn [root]. addMark) {sn [root * 2 + 1]. addMark + = sn [root]. addMark; sn [root * 2 + 1]. minVal + = sn [root]. addMark; sn [root * 2 + 2]. addMark + = sn [root]. addMark; sn [root * 2 + 2]. minVal + = sn [root]. addMark; sn [root]. minVal = 0 ;}} int SegTree: queryMin (int root, const int & qstart, const int & qend, int start, int end) {if (qstart> end | start> qend) Return maxInt; // if you do not want to submit the statement, if (start> = qstart & end <= qend) return sn [root] is returned. minVal; // If the query range is greater than the range to be queried, the minimum int mid = (start + end)/2; pushDown (root) of the range is returned ); // information of adjacent intervals can be merged into two intervals and return min (queryMin (root * 2 + 1, qstart, qend, start, mid), queryMin (root * 2 + 2, qstart, qend, mid + 1, end);} void SegTree: updateOneNode (int root, const int & index, const int & value, int start, int end) {if (start = End) {if (index = start) sn [root]. minVal + = value; else cout <there is no node of index <endl; return;} pushDown (root); int mid = (start + end)/2; if (index <= mid) updateOneNode (root * 2 + 1, index, value, start, mid); else updateOneNode (root * 2 + 2, index, value, mid + 1, end); sn [root]. minVal = min (sn [root * 2 + 1]. minVal, sn [root * 2 + 2]. minVal); // to update a leaf node, You need to trace back and update the value of its parent node}/* We first divide it into nodes in the line segment tree according to the range query method, modify the information of these nodes and Mark indicates the mark of this modification operation. When modifying and querying, if we reach a node p and decide to consider its subnodes, we need to check whether node p is marked. If yes, modify the information of its subnodes according to the tag, mark the subnodes with the same tag, and delete the p tag. */Void SegTree: updateInterval (int root, const int & ustart, const int & uend, const int & value, int start, int end) {if (uend <start | end <ustart) return; if (ustart <= start & uend> = end) {sn [root]. addMark + = value; sn [root]. minVal + = value; return;} pushDown (root); int mid = (start + end)/2; updateInterval (root * 2 + 1, ustart, uend, value, start, mid); updateInterval (root * 2 + 2, ustart, uend, value, mid + 1, end); sn [root]. minVal = min (sn [root * 2 + 1]. minVal, sn [root * 2 + 2]. minVal);} // display node void SegTree: display (int n) {for (int I = 0; I <n; I ++) {cout <sn [I]. minVal <[<sn [I]. left <, <sn [I]. right <] <endl ;}}
 

 

Main. cpp

 

/* Function: Frequently queries the minimum value of an array range, including updating the value of an array range or a node. Implementation: Line Segment tree, using an array to access a line segment tree with n nodes requires 2 ^ lg (ceil (n) + 1)-1 space, which is a balanced search tree complexity: preprocessing: create line segment tree O (n); query update time complexity O (lgN), requires additional storage space O (N) */# include
 
  
# Include SegmentionTree. husing namespace std; int main () {int iArray [6] = {2, 5, 1, 4, 9, 3}; int t = ceil (log (6.0) /log (2.0) + 1; t = pow (2.0, t)-1; SegTree st; // create a line segment tree st. buildSegTree (0, iArray, 0, 5); st. display (t); // display cout <endl; cout <st. queryMin (0, 4, 4, 0, 5) <endl; // search for the minimum value of the range [4 4] st. display (t); cout <endl; st. updateInterval (0, 0, 3,-8, 0, 5); // update range [0 3] Plus-8st. display (t); cout <endl; return 0 ;}
 
 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.