- 区域和检索 - 数组可修改
给你一个数组 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:
输入:
[“NumArray”, “sumRange”, “update”, “sumRange”]
[[[1, 3, 5]], [0, 2], [1, 2], [0, 2]]
输出:
[null, 9, null, 8]
解释:
NumArray numArray = new NumArray([1, 3, 5]);
numArray.sumRange(0, 2); // 返回 1 + 3 + 5 = 9
numArray.update(1, 2); // nums = [1,2,5]
numArray.sumRange(0, 2); // 返回 1 + 2 + 5 = 8
提示:
1 <= nums.length <= 3 * 104
-100 <= nums[i] <= 100
0 <= index < nums.length
-100 <= val <= 100
0 <= left <= right < nums.length
调用 update 和 sumRange 方法次数不大于 3 * 104
代码
class NumArray {
private:
vector<int>segmentTree;
int n;
void build(int node,int s,int e,vector<int>&nums)
{
if(s==e)//当s==e时,左右端相等,说明到了树的叶子,无需往下遍历。
{
segmentTree[node]=nums[s];//赋值
return ;
}
int m=s+(e-s)/2;//取数组中点,该点以左放左子树,以右放右子树
build(node*2+1,s,m,nums);//该节点的下一层的左节点序号为 node*2+1,向左子树进行访问。
build(node*2+2,m+1,e,nums);//该节点的下一层的右节点为node*2+1,向右子树进行访问。
segmentTree[node]=segmentTree[node*2+1]+segmentTree[node*2+2];//对每个子节点的相加,便可得到该区间的最大值。
}
void change(int index,int val,int node,int s,int e)//完成第二个函数。
{
if(s==e)//左右端相等,找到我们所要找的的节点
{
segmentTree[node]=val;//对其赋值
return ;
}
int m=s+(e-s)/2;//没找到,二分区间继续
if(index<=m)//索引小于中点值,在左子树。
{
change(index,val,node*2+1,s,m);
}else//否者,右子树。
{
change(index,val,node*2+2,m+1,e);
}
segmentTree[node]=segmentTree[node*2+1]+segmentTree[node*2+2];//其区间的最大值也要随之修改。
}
int range(int left,int right,int node,int s,int e)//完成第三个函数,返回数组 nums 中索引 left 和索引 right 之间( 包含 )的nums元素的 和
{
if(left==s&&right==e)
{
return segmentTree[node];//左右索引恰为端点。返回该节点表示区间的值。
}
int m=s+(e-s)/2;//二分
if(right<=m)
{
return range(left,right,node*2+1,s,m);//此时,右节点索引小于中点,去左子树查找。
}
else if(left>m)
{
//左节点索引大于中点,去左子树查找
return range(left,right,node*2+2,m+1,e);
} else{//否者,(其区间在两子树之间)两子树都要访问。
return range(left, m, node * 2 + 1, s, m) + range(m + 1, right, node * 2 + 2, m + 1, e);
}
}
public:
NumArray(vector<int>& nums):n(nums.size()),segmentTree(nums.size()*4)//为什么开四倍,画个满二叉树模拟一下便懂了。
{
build(0,0,n-1,nums);//建立该树
}
void update(int index, int val) {
change(index,val,0,0,n-1);
}
int sumRange(int left, int right) {
return range(left,right,0,0,n-1);
}
};
学习鸡汤:
看上去很难,但一步一步学习,由简如深,倒也积少成多,积沙成塔。
简单的来看,该算法无非是二叉树,存储的是数组的范围,将范围二分,分别放在左右子树,另有数组存储该范围的最大值。