当前位置:网站首页>Detailed explanation of line segment tree (including tested code implementation)
Detailed explanation of line segment tree (including tested code implementation)
2022-07-07 02:20:00 【A boy in the mountains】
Catalog
1. Introduction to segment tree
2. Principle and implementation of line segment tree
1. Introduction to segment tree
What is a segment tree ? Line tree is a kind of Binary search tree , And Interval tree be similar , It divides an interval into some cell intervals , Each cell interval corresponds to a leaf node in the line segment tree . [1]
For each non Leaf node [a,b], Its left son represents the interval [a,(a+b)/2], The range represented by the right son is [(a+b)/2+1,b]. So the segment tree is Balanced binary trees , The last number of child nodes is N, That is, the length of the whole segment interval .
Using line segment tree can quickly find the number of times a node appears in several line segments , Time complexity by O(logN). And not optimized Spatial complexity by 2N, So sometimes discretization is needed to compress space .----- From Baidu .
2. Principle and implementation of line segment tree
The segment tree is mainly a large interval Divide evenly Maintain between two sections , Then use the values between cells to update the large interval . In this way, the correctness can be guaranteed , It can also keep time at log Level ( Because this segment tree is balanced ). That is to say a [L,R] The interval will be divided into two intervals [L,(L+R)/2] and [(L+R)/2+1,R] Between these two communities . Then recursion goes down to divide directly L=R.
Below is a tree [ 1 , 10 ] Decomposition process of line segment tree ( Nodes of the same color are on the same layer ) As shown in the figure :We can find out , The maximum depth of this segment tree does not exceed [ log2 ( n − 1 ) ]+2.
1. The basic framework of implementation
How do we implement this structure ? Is it really a binary tree ? Of course not. We can use the heap we learned before to realize this structure . In this paper, we use full binary tree to realize segment tree , Maybe Lao tie will ask if the length of the given array is not 2 To the power of ? Here, if he is not a full binary tree, we will make him a full binary tree .
2. Calculation of space
I use large root heap to realize line generation tree , This is because the big root heap is a complete binary tree, so we can use arrays to represent , Only when the length of the array is not 2^i We need to make up . As shown in the figure
Now let's estimate how much space we need . Suppose the interval has n Elements , For a full binary tree :
1. First floor to k There are 2^k-1 Nodes ( There are about 2^k) Nodes
2. The last floor has 2^k-1 Nodes
We can figure out : The number of nodes in the last layer of the full binary tree is about the sum of the number of previous nodes
1. If n=2^i You need to 2n Nodes
2. If n=2^i+1 At this time, the worst situation requires 4*n Nodes ( Estimated value )
3. Representation :
Here, the subscript of our array is from 1 Start , This is to enable the subscripts of left and right children to use bit operations to improve efficiency . If the index of a parent node is i Then there is the following formula :
1. Left the child =2*i;
2. The right child =2*i+1
Expressed as :
1. Left the child =i<<1;
2. The right child =i<<1|1;
4. The construction of segment tree corresponds to the code :
#pragma once #include<iostream> #include<vector> using std::vector; using std::cout; using std::endl; class SegmentTree { public: SegmentTree(const vector<int>& origin) { MAXN = origin.size() + 1; arr.resize(MAXN); for (int i = 1; i < MAXN; i++) {//arr[0] Don't go from arr[1] Start arr[i] = origin[i - 1]; } sum.resize(MAXN << 2);// Equivalent to MAXN*4; To support the concept of brain tonic , The accumulation and information of a certain range lazy.resize(MAXN << 2);// To support the concept of brain tonic , One does not accumulate and mark down change.resize(MAXN << 2); update.resize(MAXN << 2); } void build(int l, int r, int rt) { if (l == r) {// There's only one number sum[rt] = arr[l]; return; } int mid = (l + r) >> 1; build(l, mid, rt << 1); build(mid + 1, r, (rt << 1) | 1);// amount to 2*rt+1; pushUp(rt);// The calculation game returns the cumulative sum upward }; void pushUp(int rt) { sum[rt] = sum[rt << 1] + sum[(rt << 1) | 1];//rt<<1|1 amount to 2*rt+1; } private: int MAXN; vector<int>arr;//arr Maintain the original sequence information from 0 Start , But here arr from 1 Start vector<int>sum;// Simulated line segment tree maintains intervals and vector<int>lazy;// Mark for accumulated lazy information vector<int>change;// The updated value of the corresponding position vector<bool>update;// To update the lazy tag };
2.1 Interval modification
The modification can be roughly divided into two steps :
1. Find this section .
2. Modify all points in this interval
The general flow is as follows :
1. If the current interval is completely covered in the target interval, this interval sum Add (r-l+1)*C
2. If it is not completely covered, the lazy flag will be sent down first
3. If the left child of this interval intersects with the target interval , Then recursively assign tasks to the left child
4. If the right child of this interval intersects with the target interval , Then recursively assign tasks to the right child .
Lazy update :
In order to improve efficiency, laziness and novelty are introduced into the line segment tree :
1. The meaning of the mark : The interval has been updated , But the subinterval has not been updated , What is the updated information ( Interval summation only records whether it has been visited , The problem of multiple operations such as the four operations of an interval needs to record which kind of operation ).
Here are two more important things : Relative mark and absolute markRelative markers : It refers to markers that can coexist , And the order of marking has nothing to do with the answer , That is, marks can be superimposed . For example, give all the numbers in a range + a , We can superimpose the marks , For example, I hit one last time Add 2 The tag , This time, give this section +5 , Then put + 2 The mark of becomes + 5
Absolute mark :
Marks that cannot coexist , Every time you have to pass the mark down , Then put a new mark on the current node . These marks cannot change the order , Otherwise it will go wrong . For example, assign a new value to a number in an interval , Or carry out multiple operations for a section .
for example :
With A sign of laziness This magical thing , When we modify the interval, we can be lazy , First modify the current node , Then hang the information directly on the node !
Such as the line segment tree below , When we want to modify the interval [ 1 , 4 ] [1,4][1,4] , Assign an element to 1 when , We can first find all nodes whose entire interval needs to be modified , It's obviously a storage area [1,3] and [4,4] These two nodes of . We can put [1,3] Of sum Change it to (3−1+1)∗1=3 , hold [ 4 , 4 ] Of sum Change it to (1-1+1)*1=1 , Then give them a value of 1 A sign of laziness , And then it's all right .
thus , Every time we modify the interval, we just need to find the target interval , There is no need to recurse down to the leaf node , So it improves efficiency .
Mark down
meet Relative markers This kind of easily bullied children , We only need to put a lazy mark on it .
however , encounter Absolute mark , Or as mentioned below Interval query , Simply marking it as lazy is obvious GG 了 . After all , A sign of laziness Simply hang a message on the node , It's impossible to encounter complicated situations !
therefore , Laziness marks Download operation It was born .
seeing the name of a thing one thinks of its function , Mark down It is to pass the lazy mark of a node to its left and right sons , Then delete the lazy flag of this node .
Let's first review the meaning of the tag :The meaning of the mark : This section has been updated , But the subinterval has not been updated , What is the updated information .
obviously , The parent interval contains child intervals , That is, the mark of the parent interval is related to the child interval . in the majority of cases , The parent and child intervals are marked same . therefore , We can deduce what mark the sub interval should be from the mark of the parent interval .
Be careful : The following problems refer to interval assignment , Unless there is any special statement .
If you want to reassign all elements in a node to x , Then its son must also be assigned x . therefore , We modify it directly at the child node sumsum value , Just change the flag of the child node ( Because the interval assignment needs Absolute mark , So when the child node has been marked , First, send down the tag of the child node , Then send down the tag of the node . But interval assignment will overwrite the value of child nodes , So in this problem , Just modify the tag directly )
Next, add C Code for :
void pushUp(int rt) {// Update father's cumulative sum sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; } void pushDown(int rt, int ln, int rn) { if (update[rt]) {//update The method needs to use update[rt << 1] = true;// Send it down update[rt << 1|1] = true; change[rt << 1] = change[rt]; change[rt << 1 | 1] = change[rt]; lazy[rt << 1] = 0;// Empty the lazy array of left and right children lazy[rt << 1 | 1] = 0; sum[rt << 1] = change[rt] * ln;// The left child always sum[rt << 1 | 1] = change[rt] * rn;// The sum of the right children update[rt] = false;// The current location has been changed to false; } if (lazy[rt]) { lazy[rt << 1] += lazy[rt]; lazy[(rt << 1) | 1] += lazy[rt]; sum[rt << 1] += lazy[rt] * ln; sum[rt << 1 | 1] += lazy[rt] * rn; lazy[rt] = 0; } } void add(int L, int R, int C, int l, int r, int rt) {//L and R Indicates the actual scope to be modified l To r Represents the actual range rt Indicates this range // I'll go to that position in the array to find the information of if (L <= l && r <= R) {// Laziness // Modify relevant information sum[rt] += C * (r - l + 1); lazy[rt] += C; return; } // The current task cannot be avoided , Unable to update , I want to send it down int mid = (l + r) >> 1; pushDown(rt, mid - l + 1, r - mid);// First send the lazy information to the left and right children if (L <= mid) {// The left child needs to assign tasks add(L, R, C, l, mid, rt << 1); } if (R > mid) {// The right child needs to issue a task add(L, R, C, mid + 1, r, rt << 1 | 1); } pushUp(rt);// Update father's cumulative sum }
about add Parameter interpretation in :
1.L,R Represents the range to be modified
2.l,r Represents the actual scope
3.C Value to add
4.rt Express l To r Get the information about the position in the array
about pushdown Interpretation of method :
1. It includes update Methods and add Lazy operation
2.2 Interval query
Sum of interval queries add similar :
1. If the current interval is completely covered in the target interval, it will be for sum The data of the location is returned
2. If it is not completely covered, the lazy flag will be sent down first
3. If the left child of this interval intersects with the target interval , Then recursively assign tasks to the left child
4. If the right child of this interval intersects with the target interval , Then recursively assign tasks to the right child .
The corresponding code :
long long query(int L, int R, int l, int r, int rt) { if (L <= l && r <= R) {// Just cover this interval and get the value directly in it return sum[rt]; } int mid = (l + r) >> 1; pushDown(rt, mid - l + 1, r - mid);// First assign lazy information to left and right children long long ans = 0; if (L <= mid) { ans+=query(L, R, l, mid, rt << 1); } if (R > mid) { ans += query(L, R, mid + 1, r, rt << 1 | 1); } return ans; }
2.3 Interval update
Interval update and interval modification are basically similar, but also different
1. If a certain interval is set to the corresponding number, then the lazy Set all the data in the array to 0
2. If you can't be lazy in the current range, you should first send lazy information to the left and right children to assign tasks
Please refer to the code for details. :
void Update(int L, int R, int C, int l, int r, int rt) { if (L <= l && r <= R) { update[rt] = true;// The tag has been updated change[rt] = C; sum[rt] = C * (r - l + 1); lazy[rt] = 0;// Empty return; } // The current task cannot be avoided , Unable to update , I want to send it down int mid = (l + r) >> 1; pushDown(rt, mid - l + 1, r - mid); if (L <= mid) { Update(L, R, C, l, mid, rt << 1); } if (R > mid) { Update(L, R, C, mid + 1, r, rt << 1 | 1); } pushUp(rt);// Update the cumulative sum of the parent node }
Corresponding total code :
#pragma once #include<iostream> #include<vector> using std::vector; using std::cout; using std::endl; class SegmentTree { public: SegmentTree(const vector<int>& origin) { MAXN = origin.size() + 1; arr.resize(MAXN); for (int i = 1; i < MAXN; i++) {//arr[0] Don't go from arr[1] Start arr[i] = origin[i - 1]; } sum.resize(MAXN << 2);// Equivalent to MAXN*4; To support the concept of brain tonic , The accumulation and information of a certain range lazy.resize(MAXN << 2);// To support the concept of brain tonic , One does not accumulate and mark down change.resize(MAXN << 2); update.resize(MAXN << 2); } void build(int l, int r, int rt) { if (l == r) { sum[rt] = arr[l]; return; } int mid = (l + r) >> 1; build(l, mid, rt << 1); build(mid + 1, r, rt << 1 | 1);// amount to 2*rt+1; pushUp(rt); }; void pushUp(int rt) {// Update father's cumulative sum sum[rt] = sum[rt << 1] + sum[rt << 1 | 1]; } void pushDown(int rt, int ln, int rn) { if (update[rt]) {//update The method needs to use update[rt << 1] = true;// Send it down update[rt << 1|1] = true; change[rt << 1] = change[rt]; change[rt << 1 | 1] = change[rt]; lazy[rt << 1] = 0;// Empty the lazy array of left and right children lazy[rt << 1 | 1] = 0; sum[rt << 1] = change[rt] * ln;// The left child always sum[rt << 1 | 1] = change[rt] * rn;// The sum of the right children update[rt] = false;// The current location has been changed to false; } if (lazy[rt]) {// Send lazy messages lazy[rt << 1] += lazy[rt]; lazy[(rt << 1) | 1] += lazy[rt]; sum[rt << 1] += lazy[rt] * ln; sum[rt << 1 | 1] += lazy[rt] * rn; lazy[rt] = 0; } } void add(int L, int R, int C, int l, int r, int rt) {//L and R Indicates the actual scope to be modified l To r Represents the actual range rt Indicates this range // I'll go to that position in the array to find the information of if (L <= l && r <= R) {// Laziness // Modify relevant information sum[rt] += C * (r - l + 1); lazy[rt] += C; return; } // The current task cannot be avoided , Unable to update , I want to send it down int mid = (l + r) >> 1; pushDown(rt, mid - l + 1, r - mid);// First send the lazy information to the left and right children if (L <= mid) {// The left child needs to assign tasks add(L, R, C, l, mid, rt << 1); } if (R > mid) {// The right child needs to issue a task add(L, R, C, mid + 1, r, rt << 1 | 1); } pushUp(rt);// Update father's cumulative sum } void Update(int L, int R, int C, int l, int r, int rt) { if (L <= l && r <= R) { update[rt] = true;// The tag has been updated change[rt] = C; sum[rt] = C * (r - l + 1); lazy[rt] = 0;// Empty return; } // The current task cannot be avoided , Unable to update , I want to send it down int mid = (l + r) >> 1; pushDown(rt, mid - l + 1, r - mid); if (L <= mid) { Update(L, R, C, l, mid, rt << 1); } if (R > mid) { Update(L, R, C, mid + 1, r, rt << 1 | 1); } pushUp(rt);// Update the cumulative sum of the parent node } long long query(int L, int R, int l, int r, int rt) { if (L <= l && r <= R) {// Just cover this interval and get the value directly in it return sum[rt]; } int mid = (l + r) >> 1; pushDown(rt, mid - l + 1, r - mid);// First assign lazy information to left and right children long long ans = 0; if (L <= mid) { ans+=query(L, R, l, mid, rt << 1); } if (R > mid) { ans += query(L, R, mid + 1, r, rt << 1 | 1); } return ans; } private: int MAXN; vector<int>arr;//arr Maintain the original sequence information from 0 Start , But here arr from 1 Start vector<int>sum;// Simulated line segment tree maintains intervals and vector<int>lazy;// Mark for accumulated lazy information vector<int>change;// The updated value of the corresponding position vector<bool>update;// To update the lazy tag };
The above code bloggers have been tested to be basically correct. If there are errors, please leave a message in the comment area !
边栏推荐
- [server data recovery] data recovery case of a Dell server crash caused by raid damage
- 猫猫回收站
- Introduction to RC oscillator and crystal oscillator
- Seconds understand the delay and timing function of wechat applet
- leetcode:736. Lisp 语法解析【花里胡哨 + 栈 + 状态enumaotu + slots】
- [unique] what is the [chain storage structure]?
- 豆瓣平均 9.x,分布式领域的 5 本神书!
- Recommended collection!! Which is the best flutter status management plug-in? Please look at the ranking list of yard farmers on the island!
- Time synchronization of livox lidar hardware -- PPS method
- Robot team learning method to achieve 8.8 times human return
猜你喜欢
FLIR blackfly s industrial camera: auto exposure configuration and code
FLIR blackfly s industrial camera: synchronous shooting of multiple cameras through external trigger
[unity] upgraded version · Excel data analysis, automatically create corresponding C classes, automatically create scriptableobject generation classes, and automatically serialize asset files
Data connection mode in low code platform (Part 1)
如何从0到1构建32Core树莓派集群
Decryption function calculates "task state and lifecycle management" of asynchronous task capability
Cisp-pte practice explanation (II)
FLIR blackfly s usb3 industrial camera: white balance setting method
Flir Blackfly S 工业相机:通过外部触发实现多摄像头同步拍摄
张平安:加快云上数字创新,共建产业智慧生态
随机推荐
GEE升级,可以实现一件run tasks
Stm32f4 --- PWM output
3D laser slam: time synchronization of livox lidar hardware
SchedulX V1.4.0及SaaS版发布,免费体验降本增效高级功能!
Cat recycling bin
Draco - glTF模型压缩利器
【LeetCode】Day97-移除链表元素
一片叶子两三万?植物消费爆火背后的“阳谋”
Yyds dry goods inventory # solve the real problem of famous enterprises: maximum difference
Decryption function calculates "task state and lifecycle management" of asynchronous task capability
【服务器数据恢复】raid损坏导致戴尔某型号服务器崩溃的数据恢复案例
猫猫回收站
ZABBIX 5.0: automatically monitor Alibaba cloud RDS through LLD
传感器:土壤湿度传感器(XH-M214)介绍及stm32驱动代码
Analyze "C language" [advanced] paid knowledge [II]
【Unity】升级版·Excel数据解析,自动创建对应C#类,自动创建ScriptableObject生成类,自动序列化Asset文件
CISP-PTE之命令注入篇
Twenty or thirty thousand a leaf? "Yang Mou" behind the explosion of plant consumption
传感器:DS1302时钟芯片及驱动代码
freeswitch拨打分机号源代码跟踪