看了就会的线段树

宝藏知识点链接

线段树:

1,相当于用树的结点表示一个区间,结点值存储与数组中,左右子节点是父节点的二分区间
2,不难发现,线段树的使用中的共性:
  • 参数传入左右端点,代表当前结点所表示的区间
  • 参数传入当前位置pos,代表当前区间的区间和所在val数组中的位置
  • 通过二分对左右子树操作(类似于树的操作)

实现代码:

递归创建,相当于后续遍历
#include <iostream>
#include<vector>
using namespace std;
class Segment_tree{
  public:
      /**
      *val为结点数组,nums为源数据,left为左区间,right为右区间
      *pos为当前区间所在结点数组(val)中位置
      */
      void build_segment_tree(vector<int> &val,vector<int> &nums,int pos,int left,int right){
              if(left==right)
                  {
                      val[pos]=nums[left];
                      return ;
                  }
              int mid=(left+right)/2;
              build_segment_tree(val,nums,pos*2+1,left,mid);
              build_segment_tree(val,nums,pos*2+2,mid+1,right);
              val[pos]=val[2*pos+1]+val[2*pos+2];
      }
区间求和
      
      /**
      *val为结点数组,left为求和左区间,right为求和右区间
      *sleft为当前遍历结点左区间,sright为当前遍历结点右区间
      *pos为当前区间所在结点数组(val)中位置
      */
      int InteravlSum(vector<int> &val,int left,int right,int sleft,int sright,int pos){
          if(left<=sleft&&right>=sright)
                  return val[pos];
          int mid=(sleft+sright)/2;
          int sum=0;
          if(left<=mid)
              sum+=InteravlSum(val,left,right,sleft,mid,pos*2+1);
          if(right>mid)
              sum+=InteravlSum(val,left,right,mid+1,sright,pos*2+2);
           //左右都没有直接返回sum=0
          return sum;
      }
更新
      
       /**
       *val为结点数组,index为更新位置,newVal为结点新值
       *sleft为当前遍历结点左区间,sright为当前遍历结点右区间
       *pos为当前区间所在结点数组(val)中位置
       *步骤:找到当前结点,更新当前结点,回溯更新父节点
       */
       void update_segment_tree(vector<int> &val,int index,int newVal,int sleft,int sright,int pos){
           if(sleft==sright&&sleft==index){
                   val[pos]=newVal;
                   return;
           }
           int mid=(sleft+sright)>>2;
           //结点在左子树上,更新左子树
           if(index<=mid)
               update_segment_tree(val,index,newVal,sleft,mid,pos*2+1);
           else
                update_segment_tree(val,index,newVal,sleft,mid,pos*2+2);
           //更改当前值
           val[pos]=val[pos*2+1]+val[pos*2+2];
       }
};
测试
int main()

{
    //长度设为4n(n为数组长度)
    int n;
    cin>>n;
    vector<int>  val(4*n);
    vector<int> nums(n);
    for(int i=0;i<n;i++)
    {
        int k;
        cin>>k;
        nums[i]=k;
    }
    Segment_tree().build_segment_tree(val,nums,0,0,n-1);
    //区间0到n-1求和
    cout<<Segment_tree().InteravlSum(val,0,n-1,0,n-1,0);
    //更新结点2
    Segment_tree().update_segment_tree(val,0,2,0,n-1,0);
    //区间0到n-1求和
    cout<<Segment_tree().InteravlSum(val,0,5,0,n-1,0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值