[Leetcode Week16]Range Sum Query - Mutable
Range Sum Query - Mutable 题解
原创文章,拒绝转载
题目来源:https://leetcode.com/problems/range-sum-query-mutable/description/
Description
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive.
The update(i, val) function modifies nums by updating the element at index i to val.
Example
Given nums = [1, 3, 5]
sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8
Solution
class NumArray {
private:
vector<int> segTree;
int size;
void pushUp(int root) {
segTree[root] = segTree[root * 2 + 1] + segTree[root * 2 + 2];
}
void build(int root, int left, int right, vector<int>& nums) {
if (left == right) {
segTree[root] = nums[left];
return;
}
int mid = (left + right) / 2;
build(root * 2 + 1, left, mid, nums);
build(root * 2 + 2, mid + 1, right, nums);
pushUp(root);
}
void updateValInInterval(int root, int left, int right, int index, int val) {
if (left == right) {
if (index == left) {
segTree[root] = val;
}
return;
}
int mid = (left + right) / 2;
if (index <= mid) {
updateValInInterval(root * 2 + 1, left, mid, index, val);
} else {
updateValInInterval(root * 2 + 2, mid + 1, right, index, val);
}
pushUp(root);
}
int queryInInterval(int root, int left, int right, int targetLeft, int targetRight) {
if (left == targetLeft && right == targetRight) {
return segTree[root];
}
int mid = (left + right) / 2;
if (targetRight <= mid)
return queryInInterval(root * 2 + 1, left, mid, targetLeft, targetRight);
else if (targetLeft >= mid + 1)
return queryInInterval(root * 2 + 2, mid + 1, right, targetLeft, targetRight);
else
return queryInInterval(root * 2 + 1, left, mid, targetLeft, mid) +
queryInInterval(root * 2 + 2, mid + 1, right, mid + 1, targetRight);
}
public:
NumArray(vector<int> nums) {
size = nums.size();
if (size > 0) {
segTree = vector<int>(size * 3);
build(0, 0, size - 1, nums);
// test
printVec(segTree);
}
}
void update(int i, int val) {
if (size == 0)
return;
updateValInInterval(0, 0, size - 1, i, val);
}
int sumRange(int i, int j) {
if (size == 0)
return 0;
return queryInInterval(0, 0, size - 1, i, j);
}
};
解题描述
这道题是典型的线段树问题,考察了线段树的构建、单节点值更新还有查询三个方面。上面给出来的解法是使用线性数据结构vector来实现线段树的做法。得出这种做法的过程中遇到的问题是,线段树数组长度的定义问题。如果说输入的数组长度为size,则线段树的节点数目确实为 2 * size - 1 ,但是实际在vector中使用下标访问子节点的时候,如果vector长度定义过短,就会出现越界的问题,这也是在提交中发现Runtime Error。后面使用内存检查工具自己运行了下测试代码就跟踪到错误发生的位置:在线段树插入新的节点的时候出现了越界。问题正是在于,下标的最大值并不是确定的,而且很有可能是大于2 * size - 1。而如果把vector开得比较大,正如上述解答中的是原数组的三倍,确实可以AC,但是却浪费了中间许多空间(实际测试中,vector中间会有一些位置没用到)。
于是想了一下,试着使用链接方式构建线段树,代码如下:
class NumArray {
private:
struct SegmentTreeNode {
SegmentTreeNode* left;
SegmentTreeNode* right;
int val;
SegmentTreeNode(int x) {
val = x;
left = NULL;
right = NULL;
}
};
int size;
SegmentTreeNode *segTreeRoot;
void pushUp(SegmentTreeNode* node) {
node -> val = node -> left -> val + node -> right -> val;
}
SegmentTreeNode* build(int left, int right, vector<int>& nums) {
if (left == right) {
return new SegmentTreeNode(nums[left]);
}
SegmentTreeNode* node = new SegmentTreeNode(0);
int mid = (left + right) / 2;
node -> left = build(left, mid, nums);
node -> right = build(mid + 1, right, nums);
pushUp(node);
return node;
}
void updateValInInterval(SegmentTreeNode* node, int left, int right, int index, int val) {
if (left == right) {
if (index == left) {
node -> val = val;
}
return;
}
int mid = (left + right) / 2;
if (index <= mid) {
updateValInInterval(node -> left, left, mid, index, val);
} else {
updateValInInterval(node -> right, mid + 1, right, index, val);
}
pushUp(node);
}
int queryInInterval(SegmentTreeNode* node, int left, int right, int targetLeft, int targetRight) {
if (left == targetLeft && right == targetRight) {
return node -> val;
}
int mid = (left + right) / 2;
if (targetRight <= mid)
return queryInInterval(node -> left, left, mid, targetLeft, targetRight);
else if (targetLeft >= mid + 1)
return queryInInterval(node -> right, mid + 1, right, targetLeft, targetRight);
else
return queryInInterval(node -> left, left, mid, targetLeft, mid) +
queryInInterval(node -> right, mid + 1, right, mid + 1, targetRight);
}
void clearTree(SegmentTreeNode* node) {
if (node != NULL) {
clearTree(node -> left);
clearTree(node -> right);
delete(node);
}
}
public:
NumArray(vector<int> nums) {
size = nums.size();
segTreeRoot = NULL;
if (size > 0) {
segTreeRoot = build(0, size - 1, nums);
}
}
void update(int i, int val) {
if (size == 0)
return;
updateValInInterval(segTreeRoot, 0, size - 1, i, val);
}
int sumRange(int i, int j) {
if (size == 0)
return 0;
return queryInInterval(segTreeRoot, 0, size - 1, i, j);
}
~NumArray() {
clearTree(segTreeRoot);
}
};
这样确实是可以节省空间,也省去了对vector长度的考虑,基本的逻辑也是相同的,但是实际提交之后,链接构建的线段树比线性结构的线段树跑出来的时间要长一些。确实,vector的底层是用数组实现,下标访问的速度肯定是要比链接构建的树要快。所以这里就需要权衡时间和空间的成本了。
[Leetcode Week16]Range Sum Query - Mutable的更多相关文章
- [LeetCode] 307. Range Sum Query - Mutable 区域和检索 - 可变
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...
- leetcode@ [307] Range Sum Query - Mutable / 线段树模板
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...
- [LeetCode] 307. Range Sum Query - Mutable 解题思路
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...
- LeetCode - 307. Range Sum Query - Mutable
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...
- Leetcode 2——Range Sum Query - Mutable(树状数组实现)
Problem: Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), ...
- leetcode 307. Range Sum Query - Mutable(树状数组)
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...
- LeetCode 308. Range Sum Query 2D - Mutable
原题链接在这里:https://leetcode.com/problems/range-sum-query-2d-mutable/ 题目: Given a 2D matrix matrix, find ...
- 【刷题-LeetCode】307. Range Sum Query - Mutable
Range Sum Query - Mutable Given an integer array nums, find the sum of the elements between indices ...
- [LeetCode] 303. Range Sum Query - Immutable 区域和检索 - 不可变
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...
随机推荐
- 从实战角度浅析snmp
Snmp Simple Network Management Protocol Snmp最终是为五花八门的网管软件服务的,由于接触的网管软件较少,所以对snmp的理解至今还仅限于初级配置阶段.以下言 ...
- Runtime之IMP指针,isa指针
要了解 isa 指针先了解下类的定义在xcode中用快捷键Shift+Cmd+O 搜索objc.h 能看到类的定义:了解 Paste_Image.png 可以看出:objc_object:Object ...
- 用select模拟一个socket server
1, 必须在非阻塞模式下,才能实现IO的多路复用,否则一个卡住就都卡住了.(单线程下的多路复用) 先检测自己,现在没有客户端连进来,所以会卡住. # 用select去模拟socket,实现单线程下的多 ...
- CentOS 输入输出重定向
标准输入重定向(STDIN,文件描述符为 0):默认从键盘输入,也可从其他文件或命令中输入.(文件描述符可以省略) 标准输出重定向(STDOUT,文件描述符为 1):默认输出到屏幕.(文件描述符可以省 ...
- Oracle 转义字符
id sfds_V_SF ASD_V_DSAF SD_V_DSAD 下划线是Oracle特殊字符,需要转移,如下 select * from systab t where t.id like ...
- 【刷题】UOJ #79 一般图最大匹配
从前一个和谐的班级,所有人都是搞OI的.有 \(n\) 个是男生,有 \(0\) 个是女生.男生编号分别为 \(1,-,n\) . 现在老师想把他们分成若干个两人小组写动态仙人掌,一个人负责搬砖另一个 ...
- BZOJ3197:[SDOI2013]刺客信条——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=3197 故事发生在1486 年的意大利,Ezio 原本只是一个文艺复兴时期的贵族,后来因为家族成员受 ...
- Dynamic Rankings——带修改区间第k大
三种做法:1.整体二分: 二分mid 考虑小于mid的修改的影响 但是大于mid的修改可能会干掉小于mid的一些值 所以额外把一个修改变成一个值的删除和一个值的添加 这样就相互独立了! 整体二分,树状 ...
- 微信小程序 事件绑定 bind和catch 区别
转自:https://blog.csdn.net/xiaoqiang_0719/article/details/79729592 本文以冒泡事件tap(手指触摸后马上离开,也就是点击事件)为例子来区别 ...
- 【树形DP】【P1364】医院放置
传送门 Description 设有一棵二叉树,如图: 其中,圈中的数字表示结点中居民的人口.圈边上数字表示结点编号,现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,同时约定,相邻接 ...