题目描述
给你一个数组 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])
做题情况
- 做出来且思路与标答一致
- 做出来但思路较为复杂
- 有思路,但时间复杂度较高无法通过 ☑
- 没有思路
自己的想法:
自己其实是想到线段树了。不过自己困在如何写线段树的模板。自己的想法是用二维数组来进行表示。但实际上可能存在一点问题。
其实想了一下,自己对于层数,以及对于如果不是一棵完全树的情况不是很了解,自己对于层数,以及对于那些递推不是很了解。
仔细看看之后,感觉实现其实也没有很难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。
不过还可以吧,希望之后自己都向线段树学习,一遍过!