【C++】线段树(一)

 【引入】

【问题】

 一个有n个数的序列,进行q次提问:第x到第y个数字(包括x,y)的总和。

在没学线段树之前,我们首先想到的肯定是遍历第x到第y个数,再把它们的和相加。

就像这样:

#include<bits/stdc++.h>
using namespace std;
int num[1000000],n,q,x,y;
int main(){
	cin>>n>>q;
	for(int i=1;i<=n;i++)cin>>num[i];
	for(int i=1;i<=q;i++){
		cin>>x>>y;
		long long ans=0;
		for(int j=x;j<=y;j++)ans+=num[j];
		cout<<ans<<endl;
	}
}

但n,q大于一定范围就会超时。所以就要用到线段树。

【概念】

线段树其实是一种树形数据结构。回到刚才的问题,如果有一种结构能贮存所有x到y的和,当问的时候直接调用就好了。于是我就想到了二叉树,比如一个{1,2,3,4,5,6,7,8,9}的序列根节点用来储存1~9左子树右子树分别贮存1~5和5~9,以此类推(如下图)。

                1~9SUM:45
                 /       \
                /         \
          1~5SUM:15        6~9SUM:30
            /   \             /     \
           /     \           /       \
     1~3SUM:6   4~5SUM:9  6~7SUM:13  8~9SUM:17
        /  \       /  \      /  \       /   \
       /    \     /    \    /    \     /     \
  1~2SUM:3   3   4      5  6      7   8       9
     /  \
    /    \
   1      2
      

【建树(build)】

我们用一维数组来储存这棵二叉树,观察上图不难发现:如果父节点为k那么左节点2k

右子节点为2k+1,所以在最坏情况下一维数组的大小是序列的4倍。用递归建树。

/*SUM是用于存贮二叉树的一维数组,num是序列*/
void Build(int l,int r,int k)
{
    if(l==r){SUM[k]=num[l];return;}//边界条件
    int mid=(l+r)/2;
    Build(l,mid,2*k);//建立右子树
    Build(mid+1,r,2*k+1);//建立左子树
    SUM[k]=SUM[2*k]+SUM[2*k+1];
}

【查找(find)】

树建完了,那要如何访问呢?用递归遍历树,返回总和。

int Find(int l,int r,int k,int ll,int rr)//ll,rr分别指x,y
{
   if(ll<=l&&r<=rr) return SUM[k];//边界条件,访问区间包括当前区间返回该和。
   int mid=(l+r)/2,sum=0;
   if(ll<=mid) sum+=Find(l,mid,2*k,ll,rr);
   if(rr>=mid+1) sum+=Find(mid+1,r,2*k+1,ll,rr);
   return sum;
}

【C++】线段树(二)

  • 17
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值