当要处理一个数组arr,求一定长度的和,并且对数组的单个值的大小进行修改。不进行特殊处理的话,不妨假设一定长度为n,那么求和的时间复杂度为O(n),修改为O(1)。
如果使用另外一个数组s,为arr的前缀和数组,那么求和的时间复杂度变为O(1),但是修改的时间复杂度变为O(n)。
这样的处理显然并不是很理想。
但使用线段树组进行处理的话,能让求和和修改的时间复杂度变为O(nlogn),从而达到减少时间复杂度的目的。
1.建立一个树状数组。
void build_tree(int arr[],int tree[],int node,int start,int endd) { if(start==endd) tree[node]=arr[endd];//递归的出口 else { int mid=(start+endd)/2; int left_node=2*node+1; int right_node=2*node+2; build_tree(arr,tree,left_node,start,mid); build_tree(arr,tree,right_node,mid+1,endd); tree[node]=tree[left_node]+tree[right_node]; } }
思路其实就是父亲等于左儿子+右儿子。
一直递归下去,直到出口。
2.对数组的区间值的询问
int query_tree(int arr[],int tree[],int node,int start,int endd,int L,int R) { printf("start=%d\n",start); printf("endd=%d\n",endd); cout<<endl; if(start>R||endd<L) { return 0; } else if(L<=start&&endd<=R) { return tree[node]; }//递归出口 else if(start==endd) { return tree[node]; } { int mid=(start+endd)/2; int left_node=2*node+1; int right_node=2*node+2; int sum_left=query_tree(arr,tree,left_node,start,mid,L,R); int sum_right=query_tree(arr,tree,right_node,mid+1,endd,L,R); return sum_left+sum_right; } }
计算【L,R】区间上的值。
从根节点开始查询,走左枝还是右枝,对于不在范围内的返回0。
3.对特定值的修改
void update_tree(int arr[],int tree[],int node,int start,int endd,int idx,int val) { if(start==endd) { arr[idx]=val; tree[node]=val; } else { int mid=(start+endd)/2; int left_node=2*node+1; int right_node=2*node+2; if(idx>=start&&idx<=mid) { update_tree(arr,tree,left_node,start,mid,idx,val); } else { update_tree(arr,tree,right_node,mid+1,endd,idx,val); } tree[node]=tree[left_node]+tree[right_node]; } }
将arr[idx]=val。先找到包含idx的那条枝,然后将其进行修改。
来源:https://www.cnblogs.com/Aracne/p/12297271.html