Data structure-line segment tree

Line tree

1. Concept (construction)

The line segment tree is a complete binary tree based on the idea of ​​dichotomy. It is used to maintain a piece of data or an array and keep the time complexity of all operations as low as O(nlogn).
For nodes with no data arranged, the default is 0, and the value stored in each node is the result of some operations performed by the left and right children (except for leaf nodes).
Insert picture description here

1. Code implementation

In fact, it can be implemented directly as an array, but in order to store the values ​​of the left and right intervals of the original array, a structure is used to implement a line segment tree

#include<iostream>
using namespace std;

const int N = 1e4 +5;
int arr[N];

struct segmentTree{
    
    
	int l,r;
	int val;
}tree[4*N]; 

void build(int p,int l,int r) {
    
    
	tree[p].l = l;
	tree[p].r = r;
	//终止条件
	if(l==r) {
    
    
		tree[p].val = arr[l];
	} 
	else {
    
    
		//本质上是递归的过程
		int mid = (l+r)/2;
		build(2*p+1,l,mid);
		build(2*p+2,mid+1,r);
		tree[p].val = tree[2*p+1].val + tree[2*p+2].val;
	}
}

int main( ) {
    
    
	for(int i=0;i<=5;i++) {
    
    
		cin>>arr[i];
	}
	build(0,0,5);
	for(int i=0;i<15;i++) {
    
    
		printf("tree[%d,%d]=%d\n",tree[i].l,tree[i].r,tree[i].val);
	}
	return 0;
}

2. Running results

The tree is followed by the interval value, some unentered nodes are all initialized to 0
Insert picture description here

2. Basic operation

1. Query (single point or interval)

Query the different situations where the interval and the node interval of the line segment tree intersect:

  1. The query interval completely contains the line segment tree node interval------------return the node tree[p].sum directly
  2. The query interval and the left child of the line segment tree node have an intersection---------recursively query(2*p+1,L,R)
  3. The query interval and the right child of the line segment tree node have an intersection ---------recursively query(2*p+2,L,R)

Single-point query is a special case of interval query, at this time L = R, just slightly modify the following code

int query(int p,int L,int R) {
    
    
	int sum = 0;
	//终止条件
	if(tree[p].l>R||tree[p].r<L)
		sum = 0;//不在范围内直接返回
	else if(tree[p].l>=L&&R>=tree[p].r)
		sum += tree[p].val;//包含其中直接返回这个节点的值
	else {
    
    	
	    //以上两种情况都不包含则是有交集的情况,对左右孩子都递归,不符合会直接返回
		int mid  = (L+R)/2;
		sum += query(2*p+1,L,R);
		sum += query(2*p+2,L,R);
	}
	return sum;
}

Test case
Insert picture description here

2. Single point modification

To modify a single point is to modify a single point query. After the modified node value is found in the query, the value of each ancestor node is updated.

//单点修改 
void update(int p,int ind,int val) {
    
    
	if(ind < tree[p].l||ind > tree[p].r ) 
		return;
	else if(tree[p].r == tree[p].l) {
    
    
		tree[p].val = val;
	}
	else {
    
    
		update(2*p+1,ind,val);
		update(2*p+2,ind,val);
		//这一步很重要,一定不能少
		tree[p].val = tree[2*p+1].val + tree[2*p+2].val;
	}
}

Insert picture description here

3. Interval modification (with pushdown())

Interval modification means that the value of a certain interval must be modified. It may be the operation of adding one to all the values ​​of a certain interval. If you modify each participating ancestor and descendant node, it will time out. At this time, the line can only Change the ancestor and modify the child nodes when they are needed. Use a lazy mark (delay mark), and then use the mark to operate when you need it.

//区间修改,此处是将[L,R]所有元素全部加一 
void change(int p,int L,int R) {
    
    
	if(R < tree[p].l||L > tree[p].r ) 
		return;
	if(L <= tree[p].l && tree[p].r <= R) {
    
    
		//完全包含其中,直接改变标记并处理然后返回即可 
		tree[p].val += (tree[p].r - tree[p].l +1);
		tree[p].lazy += 1;
		return;
	}
	//处理延迟标记,因为要用到左右儿子节点了 
	pushdown(p);
	int mid = (tree[p].l + tree[p].r)/2;
	if(L <= mid)	change(2*p+1,L,R);
	if(R >  mid) 	change(2*p+2,L,R);
	tree[p].val = tree[2*p+1].val + tree[2*p+2].val;
}

Next, we will introduce the pushdown() function. The function of this function is to process the delay mark. For the marked node, that is, lazy is not 0, the mark is passed to the left and right sons, and then the content of the left and right sons is processed, and finally this The flag is cleared because it has been processed.

void pushdown(int p) {
    
    
	if(tree[p].lazy != 0){
    
    
		tree[2*p+1].lazy += tree[p].lazy;
		tree[2*p+2].lazy += tree[p].lazy;
		int mid = (tree[p].l + tree[p].r)/2;
		tree[2*p+1].val += mid - tree[p].l + 1;
		tree[2*p+2].val += tree[p].r - mid;
		tree[p].lazy = 0;//父节点的标记清零 
	}
}

Finally, because the interval modification uses the lazy mark, the query at this time is no longer the query above. It is also the query function that needs the pushdown() function, but the function has not changed in general.

//查询 
int _query(int p,int L,int R) {
    
    
	//终止条件
	if(tree[p].l>R||tree[p].r<L)
		return 0;
		
	int sum = 0;
	pushdown(p);
	if(tree[p].l>=L&&R>=tree[p].r) {
    
    
		sum += tree[p].val;
	} 		
	else {
    
    	
		int mid  = (L+R)/2;
		sum += _query(2*p+1,L,R);
		sum += _query(2*p+2,L,R);
	}
	return sum;
}

3. Special line segment tree

There are also two more special multiplication line segment trees and root number line segment trees, which will be added later. . .

Guess you like

Origin blog.csdn.net/weixin_45203704/article/details/107256918