307. 区域和检索 - 数组可修改

题目描述

给你一个数组 nums,请你完成两类查询。

其中一类查询要求 更新 数组 nums下标对应的值
另一类查询要求返回数组 nums中索引 left和索引 right之间( 包含 )的nums元素的 和 ,其中 left <= right
实现 NumArray 类:

NumArray(int[] nums) 用整数数组 nums 初始化对象
void update(int index, int val)nums[index]的值 更新 为 val
int sumRange(int left, int right)返回数组 nums中索引 left 和索引 right 之间( 包含 )的nums元素的 和 (即,nums[left] + nums[left + 1], ..., nums[right])

做题情况

  1. 做出来且思路与标答一致
  2. 做出来但思路较为复杂
  3. 有思路,但时间复杂度较高无法通过 ☑
  4. 没有思路

自己的想法:

自己其实是想到线段树了。不过自己困在如何写线段树的模板。自己的想法是用二维数组来进行表示。但实际上可能存在一点问题。
其实想了一下,自己对于层数,以及对于如果不是一棵完全树的情况不是很了解,自己对于层数,以及对于那些递推不是很了解。
仔细看看之后,感觉实现其实也没有很难mmm。

标答:线段树

只需要维护一个树状结构。且因为是完全二叉树,所以我们可以用数组来储存树。在每个节点,我们储存所有子节点的和即可。
在建树时,我们只需要从树节点开始逐层往上推即可。
我们首先初始化最下面一层。可以预见的,最下面一层后面均为空,我们用0补充完整即可。(因为之后涉及到了树节点相加向上求和)
需要注意的是每层的表示(因为我们在一整个数组中表示每一层的,所以需要准确定位从哪里开始的,以及到哪里结束。

实际代码

class NumArray {
public:
    //这个自己印象还挺深的,是用线段树的方式来解决
    //线段树的思想就是改logn数目的块
    //自己不习惯的是在二叉树中的位置
    //其实差不多,不过这里是用树的形式,不过也是用数组来进行储存的
    vector<int> vec;
    int n;
    int m;
    NumArray(vector<int>& nums) 
    {
        n=nums.size();
        m=ceil(log(n)/log(2));
        vec.resize(pow(2,m+1),0);
        for (int j=0;j<n;++j) vec[j+pow(2,m)-1]=nums[j];
        for (int i=m-1;i>=0;--i)
        {
            for (int j=pow(2,i)-1;j<=pow(2,i+1)-2;++j)
            {
                vec[j]=vec[2*j+1]+vec[2*j+2];
            }
        }
    }
    
    void update(int index, int val) 
    {
        int pos=index+pow(2,m)-1;
        int cha=val-vec[pos];
        vec[pos]=val;
        while (pos!=0)
        {
            pos=(pos-1)/2;
            vec[pos]+=cha;
        }
    }

    int sum(vector<int>& vec,int left,int right)
    {
        if (left==right) return vec[left];
        else if (left>right) return 0;
        int res=0;
        if (left%2==0) 
        {
            res+=vec[left];
            left++;
        }
        if (right%2==1)
        {
            res+=vec[right];
            right--;
        }
        return res+sum(vec,left/2,(right-1)/2);
    }
    
    int sumRange(int left, int right) 
    {
        int posleft=left+pow(2,m)-1;
        int posright=right+pow(2,m)-1;
        return sum(vec,posleft,posright);
    }
};

总结

对于线段树,自己还是比较满意的。第一次只是抄了一遍,这次可以自己写出来了。
但还是不够满意,看了标答,写的好优美hh。自己虽然写出来了,但其实不够优美mmm。
不过还可以吧,希望之后自己都向线段树学习,一遍过!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值