线段树
解决问题:
在数组区间中
更新某个数字(updata);
求数组某区间和(query);
解决方法一and 二
一. 直接用数组
方法:略
分析:
- updata复杂度是O(1)
-但 query复杂度O(n)
二. 前缀和
方法:
sum_a[i]=a[1]+...+a[i]
求解区间和时:a[j]+...a[i]=sum_a[i]-sum_a[j]
分析
- query复杂度O(1)
- 但updata复杂度O(n)
理解
每个叶子结点的值就是数组的值,
每个非叶子结点代表某区间和,
且左右两个孩子分别存储父亲一半的区间。
每个父亲的存储的值也就是两个孩子存储的值的和。
求和过程query
更改数据过程updata
建树
(不懂去看堆排序那章)
补成完全二叉树,用数组表示。
因此有性质:
(left代表左儿子,right代表右儿子)
代码
#include <bits/stdc++.h> using namespace std; #define MAX_LEN 1000 //node树的根节点 void build_tree(int arr[],int tree[],int node,int start,int end) { if(start == end) { tree[node]=arr[start]; return; } int mid = (start+end)/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,end); tree[node]=tree[left_node]+tree[right_node]; } int main() { int arr[] = {1,3,5,7,9,11}; int size = 6; int tree[MAX_LEN] = {0}; build_tree(arr,tree,0,0,size-1); int i; for(i=0;i<=14;i++) { cout<<"tree["<<i<<"]:"<<tree[i]<<endl; } return 0; }
updata(更新数据)
void updata_tree(int arr[],int tree[],int node,int start,int end,int idx,int val) { if(start == end) { arr[idx]=val; tree[node] = val; return; } int mid = (start + end) / 2; int left_node = 2*node +1; int right_node =2*node + 2; if(idx>start && idx<=mid) { updata_tree(arr,tree,left_node,start,mid,idx,val); } else { updata_tree(arr,tree,right_node,mid+1,end,idx,val); } tree[node]=tree[left_node]+tree[right_node]; }
query_tree 查询
int query_tree(int arr[],int tree[],int node,int start,int end,int L,int R) { if(R<start||L>end)//不在计算范围之内 { return 0; } else if(start ==end) { return tree[node]; } int mid = (start+end)/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,end,L,R); return sum_left+sum_right; }
问题出现在:递归出口是 单个结点;实际上只需要startend在RL之间即可返回
优化:
int query_tree(int arr[],int tree[],int node,int start,int end,int L,int R) { if(R<start||L>end)//不在计算范围之内 { return 0; } else if(start>=L&&end<=R) { return tree[node]; } else if(start ==end) { return tree[node]; } int mid = (start+end)/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,end,L,R); return sum_left+sum_right; }
整体代码
#include <bits/stdc++.h> using namespace std; #define MAX_LEN 1000 //node树的根节点 void build_tree(int arr[],int tree[],int node,int start,int end) { if(start == end) { tree[node]=arr[start]; return; } int mid = (start+end)/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,end); tree[node]=tree[left_node]+tree[right_node]; } void updata_tree(int arr[],int tree[],int node,int start,int end,int idx,int val) { if(start == end) { arr[idx]=val; tree[node] = val; return; } int mid = (start + end) / 2; int left_node = 2*node +1; int right_node =2*node + 2; if(idx>start && idx<=mid) { updata_tree(arr,tree,left_node,start,mid,idx,val); } else { updata_tree(arr,tree,right_node,mid+1,end,idx,val); } tree[node]=tree[left_node]+tree[right_node]; } int query_tree(int arr[],int tree[],int node,int start,int end,int L,int R) { if(R<start||L>end)//不在计算范围之内 { return 0; } else if(start>=L&&end<=R) { return tree[node]; } else if(start ==end) { return tree[node]; } int mid = (start+end)/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,end,L,R); return sum_left+sum_right; } int main() { int arr[] = {1,3,5,7,9,11}; int size = 6; int tree[MAX_LEN] = {0}; build_tree(arr,tree,0,0,size-1); int i; for(i=0;i<=14;i++) { cout<<"tree["<<i<<"]:"<<tree[i]<<endl; } updata_tree(arr,tree,0,0,size-1,4,6); cout<<endl; cout<<"up_data:"<<endl; for(i=0;i<=14;i++) { cout<<"tree["<<i<<"]:"<<tree[i]<<endl; } int s = query_tree(arr,tree,0,0,size-1,2,5); cout<<endl<<"query_tree: "<<s<<endl; return 0; }
实战
模板题
C - 数组计算机
代码
#include <bits/stdc++.h> using namespace std; #define ll long long #define maxn 100007 long long int a[maxn]; long long int tree[4*maxn]; void build_tree(ll a[],ll tree[],int node,int start,int end) { if(start==end) { tree[node]=a[start]; return; } int mid = (start+end)/2; int left_node = 2*node+1; int right_node = 2*node+2; build_tree(a,tree,left_node,start,mid); build_tree(a,tree,right_node,mid+1,end); tree[node]=tree[left_node]+tree[right_node]; } void updata(ll a[],ll tree[],int node,int start,int end,int p,int v) { if(start==end)//此时头==尾==p+1但是不等于p { //cout<<start<<"**9"<<endl; tree[node]+=v; a[p]+=v; return; } int mid = (start+end)/2; int left_node = 2*node+1; int right_node = 2*node+2; if(p>start && p<=mid) { updata(a,tree,left_node,start,mid,p,v); } else //if(p>mid&&p<end) { updata(a,tree,right_node,mid+1,end,p,v); } tree[node]=tree[left_node]+tree[right_node]; } ll query(ll a[],ll tree[],int node,int start,int end,int L,int R) { if(R<start||L>end)//不在计算范围之内 { return 0; } else if(start>=L&&end<=R) { return tree[node]; } else if(start ==end) { return tree[node]; } int mid=(start+end)/2; int left_node = 2*node+1; int right_node = 2*node+2; ll sum_left= query(a,tree,left_node,start,mid,L,R); ll sum_right= query(a,tree,right_node,mid+1,end,L,R); return sum_left+sum_right; } int main() { int n; while(scanf("%d",&n)!=EOF) { memset(tree,0,sizeof(tree));//清空树 int i; for(i=1; i<=n; i++) { scanf("%lld",&a[i]);//数组 } build_tree(a,tree,0,1,n); int q; scanf("%d",&q); long long int p,v; int L,R; while(q--) { int key; scanf("%d",&key); if(key==1) { scanf("%lld %lld",&p,&v); updata(a,tree,0,1,n,p,v); } else if(key==2) { scanf("%d%d",&L,&R); printf("%lld\n",query(a,tree,0,1,n,L,R)); } } } return 0; }
D - 效率至上
思路:
套用线段树模板,在线段树里加两个值,分别记录当前区间内的最小值和最大值,然后只需要每次选出区间内的最大值和最小值,将他们做减法输出即可。
本文作者:kingwzun
本文链接:https://www.cnblogs.com/kingwz/p/15177756.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步