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. HashMap源码剖析及实现原理分析(学习笔记)

    一.需求 最近开发中,总是需要使用HashMap,而为了更好的开发以及理解HashMap:因此特定重新去看HashMap的源码并写下学习笔记,以便以后查阅. 二.HashMap的学习理解 1.我们首先 ...

  2. asp.net中缓存的使用

    刚学到asp.net怎么缓存,这里推荐学习一下 www.cnblogs.com/wang726zq/archive/2012/09/06/cache.html http://blog.csdn.net ...

  3. Dwango Programming Contest V 翻车记

    A:签到. #include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> ...

  4. Luogu1092 NOIP2004虫食算(搜索+高斯消元)

    暴力枚举每一位是否进位,然后就可以高斯消元解出方程了.然而复杂度是O(2nn3),相当不靠谱. 考虑优化.注意到某一位进位情况的变化只会影响到方程的常数项,于是可以在最开始做一次高斯消元算出每个未知数 ...

  5. hadoop 编码实现文件传输、查看等基本文件控制

    hadoop集群搭建参考:https://www.cnblogs.com/asker009/p/9126354.html 1.创建一个maven工程,添加依赖 <?xml version=&qu ...

  6. NOIP2016Day1T3换教室(floyd+期望dp)

    啊...这个时间写博客,明天还要上学,整个人都不好了... 这是我写的第一道期望题hiahiahia... 题目大意就不说了QWQ 80分儿做法:先floyd,爆搜枚举哪些点取,求出答案,效率O(C( ...

  7. 在CentOS6.5 下安装并使用Java开发opencv的配置(一)

    1) 安装gcc以及cmake等等乱七八糟的软件 yum install gcc yum install python yum install cmake yum groupinstall " ...

  8. HDU 4348 主席树区间更新

    To the moon Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total ...

  9. HDU5533(水不水?)

    Dancing Stars on Me Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Ot ...

  10. 【Android】完善Android学习(六:API 4.0)

    备注:之前Android入门学习的书籍使用的是杨丰盛的<Android应用开发揭秘>,这本书是基于Android 2.2API的,目前Android已经到4.4了,更新了很多的API,也增 ...