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的更多相关文章

  1. [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 ...

  2. 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 ...

  3. [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 ...

  4. 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 ...

  5. 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), ...

  6. 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 ...

  7. LeetCode 308. Range Sum Query 2D - Mutable

    原题链接在这里:https://leetcode.com/problems/range-sum-query-2d-mutable/ 题目: Given a 2D matrix matrix, find ...

  8. 【刷题-LeetCode】307. Range Sum Query - Mutable

    Range Sum Query - Mutable Given an integer array nums, find the sum of the elements between indices ...

  9. [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 ...

随机推荐

  1. asp.netMVC中实现分页方法

    方法一:使用传统的sql语句实现分页,    public class UserprintDao如下 /// <summary> /// 取得用户申请记录列表(按分页) /// </ ...

  2. query 获取本身的HTML

    <div class="test"><p>hello,你好!</p></div> <script> $(".t ...

  3. iOS-学习UIKIt框架的重要性

      前言: 众所周知,我们的移动设备的屏幕上可以展示很多图形界面,作为用户的我们可以通过屏幕上的图形界面浏览信息,也可以通过与图形界面的简单交互,在移动设备上实现各种各样的功能操作.....可以说,没 ...

  4. BZOJ1058:[ZJOI2007]报表统计——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=1058 https://www.luogu.org/problemnew/show/P1110#su ...

  5. BZOJ3052 & UOJ58:[WC2013]糖果公园——题解

    http://uoj.ac/problem/58 http://www.lydsy.com/JudgeOnline/problem.php?id=3052 输入格式 输出格式 input 4 3 5 ...

  6. NOIP2018普及&提高题解

    第一次考$NOIP$的我已经自闭了 $CCF$告诉我们了一件事情,要对自己写的程序有信仰,相信$CCF$的数据是水的 普及组: 分数:$100+100+30+100=330$ $1.titile$: ...

  7. 使用httpclient post请求中文乱码解决办法

    使用httpclient post请求中文乱码解决办法   在使用httpclient发送post请求的时候,接收端中文乱码问题解决. 正文: 我们都知道,一般情况下使用post请求是不会出现中文乱码 ...

  8. squid总结

    squid可以完成的工作: 代理服务器 反向代理服务器 防火墙 缓存功能 透明代理 squid和varnish的对比,以及squid的优缺点说明: 缓存到硬盘,容易遇到I/O瓶颈 V3.2以下不支持多 ...

  9. stout代码分析之十:c++11之move和forward

    stout中大量使用了c++11的特性,而c++11中move和forward大概是最神奇的特性了. 左值和右值的区别 ; // a是左值,0是右值 int b = rand(); // b是左值,r ...

  10. log4j输出到数据库(输出自定义参数、分级保存)

    转载自:http://wallimn.iteye.com/blog/1525819 Log4J日志输出到数据库中,且保存些用户自定义的参数,如用户ID,且配置仅输出指定级别的日志.  配置文件如下:  ...