HELLO WORLD--一起加油(🍺)!|

kingwzun

园龄:3年6个月粉丝:111关注:0

2021-08-24 08:36阅读: 45评论: 0推荐: 0

线段树

解决问题:

在数组区间中
更新某个数字(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)

理解

每个叶子结点的值就是数组的值,
每个非叶子结点代表某区间和,
且左右两个孩子分别存储父亲一半的区间。
每个父亲的存储的值也就是两个孩子存储的值的和。
image
求和过程query
image

更改数据过程updata
image

建树

(不懂去看堆排序那章)

补成完全二叉树,用数组表示。
image
因此有性质:

image
(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 中国大陆许可协议进行许可。

posted @   kingwzun  阅读(45)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起