线段树-建树、修改、查询

线段树(Segment Tree)

一、创建线段树

在这里插入图片描述
在这里插入图片描述

//创建线段树 
void buildtree(int arr[],int tree[],int node,int start,int end){
    
    
	
	//递归到树的叶子结点、结点值为单个数组元素 
	if(start==end)
		//修改tree数组结点值 
		tree[node]=arr[start];
		
	else{
    
    
		
		//区间分段点 
		int mid=(start+end)/2;
		//左右结点下标(层序,根结点下标为0) 
		int left_node=node*2+1;
		int right_node=node*2+2; 
		
		//创建左右子树 
		buildtree(arr,tree,left_node,start,mid);
		buildtree(arr,tree,right_node,mid+1,end);
		
		//修改根结点的值(根结点=左结点+右结点) 
		tree[node]=tree[left_node]+tree[right_node]; 	
	}
}

二、修改arr[]元素,更新tree[]元素值

在这里插入图片描述
在这里插入图片描述

//修改数组元素,arr[idx]=val
void update(int arr[],int tree[],int node,int start,int end,int idx,int val){
    
    
	
	//找到待修改结点arr[idx],从叶子结点往上依次修改tree数组元素 
	if(start==end){
    
    
		arr[idx]=val;
		tree[node]=val;
	}
	
	else
	{
    
    
		int mid=(start+end)/2;
		int left_node=node*2+1;
		int right_node=node*2+2;
		
		//寻找待修改元素的下标idx所在的区间 
		//idx在左区间 
		if(idx>=start&&idx<=mid)
			update(arr,tree,left_node,start,mid,idx,val);
		else
			//idx在右区间 
			update(arr,tree,right_node,mid+1,end,idx,val);
			
		tree[node]=tree[left_node]+tree[right_node];		
	}
}

三、求区间和

需要访问的区间[L-R]和[start-end]没有重叠的部分,直接return 0;([L-R]在[start-end]左、右;两种情况)

在这里插入图片描述
在这里插入图片描述

//查询数组区间和
int query(int arr[],int tree[],int node,int start,int end,int L,int R){
    
    
	
//	printf("%d %d\n",start,end);

	//需要访问的区间[L-R]和[start-end]没有重叠的部分
	if(start>R||L>end)
		return 0;
		
	//[start-end]在[L,R]之内,当前结点即为[start-end]区间和,
	//没必要递归至叶结点,直接返回结点值 
	if(L<=start&&end<=R)
		return tree[node];
		
	if(start==end)
		return tree[node];
		
	//查询区间和左右子区间均有重叠部分 
	else
	{
    
    
		int mid=(start+end)/2;
		int left_node=node*2+1;
		int right_node=node*2+2;
		
		//将[start-end]分成左右、区间,求左右区间和 
		int sum_left=query(arr,tree,left_node,start,mid,L,R);
		int sum_right=query(arr,tree,right_node,mid+1,end,L,R);
		
		return sum_left+sum_right;
	}
}

四、时间复杂度降为O(logn)

五、完整代码

#include<bits/stdc++.h> 
using namespace std;
const int max_len=1000;

int tree[max_len];

//创建线段树 
void buildtree(int arr[],int tree[],int node,int start,int end){
    
    
	
	//递归到树的叶子结点、结点值为单个数组元素 
	if(start==end)
		//修改tree数组结点值 
		tree[node]=arr[start];
		
	else{
    
    
		
		//区间分段点 
		int mid=(start+end)/2;
		//左右结点下标(层序,根结点下标为0) 
		int left_node=node*2+1;
		int right_node=node*2+2; 
		
		//创建左右子树 
		buildtree(arr,tree,left_node,start,mid);
		buildtree(arr,tree,right_node,mid+1,end);
		
		//修改根结点的值(根结点=左结点+右结点) 
		tree[node]=tree[left_node]+tree[right_node]; 	
	}
}

//修改数组元素,arr[idx]=val
void update(int arr[],int tree[],int node,int start,int end,int idx,int val){
    
    
	
	//找到待修改结点arr[idx],从叶子结点往上依次修改tree数组元素 
	if(start==end){
    
    
		arr[idx]=val;
		tree[node]=val;
	}
	
	else
	{
    
    
		int mid=(start+end)/2;
		int left_node=node*2+1;
		int right_node=node*2+2;
		
		//寻找待修改元素的下标idx所在的区间 
		//idx在左区间 
		if(idx>=start&&idx<=mid)
			update(arr,tree,left_node,start,mid,idx,val);
		else
			//idx在右区间 
			update(arr,tree,right_node,mid+1,end,idx,val);
			
		tree[node]=tree[left_node]+tree[right_node];		
	}
} 

//查询区间和
int query(int arr[],int tree[],int node,int start,int end,int L,int R){
    
    
	
//	printf("%d %d\n",start,end);

	//需要访问的区间[L-R]和[start-end]没有重叠的部分
	if(start>R||L>end)
		return 0;
		
	//[start-end]在[L,R]之内,当前结点即为[start-end]区间和,
	//没必要递归至叶结点,直接返回结点值 
	if(L<=start&&end<=R)
		return tree[node];
		
	if(start==end)
		return tree[node];
		
	//查询区间和左右子区间均有重叠部分 
	else
	{
    
    
		int mid=(start+end)/2;
		int left_node=node*2+1;
		int right_node=node*2+2;
		
		//将[start-end]分成左右、区间,求左右区间和 
		int sum_left=query(arr,tree,left_node,start,mid,L,R);
		int sum_right=query(arr,tree,right_node,mid+1,end,L,R);
		
		return sum_left+sum_right;
	}
}
 
int main()
{
    
    
	int n;
	cin>>n;
	int arr[n];
	for(int i=0;i<n;i++)
		cin>>arr[i];
		
	buildtree(arr,tree,0,0,n-1);
	
	//创建线段树 
	for(int i=0;i<15;i++)//n=6
		printf("tree[%d]=%d\n",i,tree[i]);
	
	cout<<endl;
	
	//修改数组元素
	update(arr,tree,0,0,n-1,4,6);
	for(int i=0;i<15;i++)//n=6
		printf("tree[%d]=%d\n",i,tree[i]);
		
	cout<<endl;
	
	//查询区间和
	int add=query(arr,tree,0,0,n-1,2,5);//区间[2-5]的元素和:arr[2]+...+arr[5]
	cout<<add<<endl;
	 
	return 0;
}

//在B站上面看到的一个讲得非常棒的视频,直接搬了过来。。

猜你喜欢

转载自blog.csdn.net/Amethystlry/article/details/107550839