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


  1. class NumArray {
  2. private:
  3. vector<int> segTree;
  4. int size;
  5. void pushUp(int root) {
  6. segTree[root] = segTree[root * 2 + 1] + segTree[root * 2 + 2];
  7. }
  8. void build(int root, int left, int right, vector<int>& nums) {
  9. if (left == right) {
  10. segTree[root] = nums[left];
  11. return;
  12. }
  13. int mid = (left + right) / 2;
  14. build(root * 2 + 1, left, mid, nums);
  15. build(root * 2 + 2, mid + 1, right, nums);
  16. pushUp(root);
  17. }
  18. void updateValInInterval(int root, int left, int right, int index, int val) {
  19. if (left == right) {
  20. if (index == left) {
  21. segTree[root] = val;
  22. }
  23. return;
  24. }
  25. int mid = (left + right) / 2;
  26. if (index <= mid) {
  27. updateValInInterval(root * 2 + 1, left, mid, index, val);
  28. } else {
  29. updateValInInterval(root * 2 + 2, mid + 1, right, index, val);
  30. }
  31. pushUp(root);
  32. }
  33. int queryInInterval(int root, int left, int right, int targetLeft, int targetRight) {
  34. if (left == targetLeft && right == targetRight) {
  35. return segTree[root];
  36. }
  37. int mid = (left + right) / 2;
  38. if (targetRight <= mid)
  39. return queryInInterval(root * 2 + 1, left, mid, targetLeft, targetRight);
  40. else if (targetLeft >= mid + 1)
  41. return queryInInterval(root * 2 + 2, mid + 1, right, targetLeft, targetRight);
  42. else
  43. return queryInInterval(root * 2 + 1, left, mid, targetLeft, mid) +
  44. queryInInterval(root * 2 + 2, mid + 1, right, mid + 1, targetRight);
  45. }
  46. public:
  47. NumArray(vector<int> nums) {
  48. size = nums.size();
  49. if (size > 0) {
  50. segTree = vector<int>(size * 3);
  51. build(0, 0, size - 1, nums);
  52. // test
  53. printVec(segTree);
  54. }
  55. }
  56. void update(int i, int val) {
  57. if (size == 0)
  58. return;
  59. updateValInInterval(0, 0, size - 1, i, val);
  60. }
  61. int sumRange(int i, int j) {
  62. if (size == 0)
  63. return 0;
  64. return queryInInterval(0, 0, size - 1, i, j);
  65. }
  66. };

解题描述

这道题是典型的线段树问题,考察了线段树的构建、单节点值更新还有查询三个方面。上面给出来的解法是使用线性数据结构vector来实现线段树的做法。得出这种做法的过程中遇到的问题是,线段树数组长度的定义问题。如果说输入的数组长度为size,则线段树的节点数目确实为 2 * size - 1 ,但是实际在vector中使用下标访问子节点的时候,如果vector长度定义过短,就会出现越界的问题,这也是在提交中发现Runtime Error。后面使用内存检查工具自己运行了下测试代码就跟踪到错误发生的位置:在线段树插入新的节点的时候出现了越界。问题正是在于,下标的最大值并不是确定的,而且很有可能是大于2 * size - 1。而如果把vector开得比较大,正如上述解答中的是原数组的三倍,确实可以AC,但是却浪费了中间许多空间(实际测试中,vector中间会有一些位置没用到)。

于是想了一下,试着使用链接方式构建线段树,代码如下:


  1. class NumArray {
  2. private:
  3. struct SegmentTreeNode {
  4. SegmentTreeNode* left;
  5. SegmentTreeNode* right;
  6. int val;
  7. SegmentTreeNode(int x) {
  8. val = x;
  9. left = NULL;
  10. right = NULL;
  11. }
  12. };
  13. int size;
  14. SegmentTreeNode *segTreeRoot;
  15. void pushUp(SegmentTreeNode* node) {
  16. node -> val = node -> left -> val + node -> right -> val;
  17. }
  18. SegmentTreeNode* build(int left, int right, vector<int>& nums) {
  19. if (left == right) {
  20. return new SegmentTreeNode(nums[left]);
  21. }
  22. SegmentTreeNode* node = new SegmentTreeNode(0);
  23. int mid = (left + right) / 2;
  24. node -> left = build(left, mid, nums);
  25. node -> right = build(mid + 1, right, nums);
  26. pushUp(node);
  27. return node;
  28. }
  29. void updateValInInterval(SegmentTreeNode* node, int left, int right, int index, int val) {
  30. if (left == right) {
  31. if (index == left) {
  32. node -> val = val;
  33. }
  34. return;
  35. }
  36. int mid = (left + right) / 2;
  37. if (index <= mid) {
  38. updateValInInterval(node -> left, left, mid, index, val);
  39. } else {
  40. updateValInInterval(node -> right, mid + 1, right, index, val);
  41. }
  42. pushUp(node);
  43. }
  44. int queryInInterval(SegmentTreeNode* node, int left, int right, int targetLeft, int targetRight) {
  45. if (left == targetLeft && right == targetRight) {
  46. return node -> val;
  47. }
  48. int mid = (left + right) / 2;
  49. if (targetRight <= mid)
  50. return queryInInterval(node -> left, left, mid, targetLeft, targetRight);
  51. else if (targetLeft >= mid + 1)
  52. return queryInInterval(node -> right, mid + 1, right, targetLeft, targetRight);
  53. else
  54. return queryInInterval(node -> left, left, mid, targetLeft, mid) +
  55. queryInInterval(node -> right, mid + 1, right, mid + 1, targetRight);
  56. }
  57. void clearTree(SegmentTreeNode* node) {
  58. if (node != NULL) {
  59. clearTree(node -> left);
  60. clearTree(node -> right);
  61. delete(node);
  62. }
  63. }
  64. public:
  65. NumArray(vector<int> nums) {
  66. size = nums.size();
  67. segTreeRoot = NULL;
  68. if (size > 0) {
  69. segTreeRoot = build(0, size - 1, nums);
  70. }
  71. }
  72. void update(int i, int val) {
  73. if (size == 0)
  74. return;
  75. updateValInInterval(segTreeRoot, 0, size - 1, i, val);
  76. }
  77. int sumRange(int i, int j) {
  78. if (size == 0)
  79. return 0;
  80. return queryInInterval(segTreeRoot, 0, size - 1, i, j);
  81. }
  82. ~NumArray() {
  83. clearTree(segTreeRoot);
  84. }
  85. };

这样确实是可以节省空间,也省去了对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. 每个zone的low memory是怎么计算出来的

    内核都是试图让活动页和不活动页的数量均衡 在分配内存时每次都会唤醒wakeup_swapd,这个函数会在 现在是不是已经没有全局的LRU表了?已经都变成per cgroup级别的LRU表了吗? ina ...

  2. TCP标志位简析

    TCP标志位简析   TCP标志位  URG:此标志表示TCP包的紧急指针域(后面马上就要说到)有效,用来保证TCP连接不被中断,并且督促中间层设备要尽快处理这些数据: ACK:此标志表示应答域有效, ...

  3. wpf DataGrid加载行号

    <DataGrid Name="tkdg" HorizontalContentAlignment="Center" AutoGenerateColumns ...

  4. WPF如何将数据库中的二进制图片数据显示在Image控件上

    首先在xaml文件里定义一个Image控件,取名为img MemoryStream stream = new MemoryStream(获得的数据库对象): BitMapImage bmp = new ...

  5. Go语言【第十三篇】:Go语言递归函数

    Go语言递归函数 递归,就是在运行的过程中调用自己,语法格式如下: func recursion() { recursion() /* 函数调用自身 */ } func main() { recurs ...

  6. 【刷题】BZOJ 1002 [FJOI2007]轮状病毒

    Description 轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的.一个N轮状基由圆环上N个不同的基原子 和圆心处一个核原子构成的,2个原子之间的边表示这2个原子之间的信息通道.如下 ...

  7. 【刷题】洛谷 P3804 【模板】后缀自动机

    题目描述 给定一个只包含小写字母的字符串 \(S\) , 请你求出 \(S\) 的所有出现次数不为 \(1\) 的子串的出现次数乘上该子串长度的最大值. 输入输出格式 输入格式: 一行一个仅包含小写字 ...

  8. Android 打开照相机、获取相册图片、获取图片并裁减

    一.调用照相机 注:surfaceView在当Activity不在前台的时候,会被销毁(onPause方法之后,执行销毁方法)当Activity回到前台时,在Activity执行onResume方法之 ...

  9. 【BZOJ1941】Hide and Seek(KD-Tree)

    [BZOJ1941]Hide and Seek(KD-Tree) 题面 BZOJ 洛谷 题解 \(KD-Tree\)对于每个点搜一下最近点和最远点就好了 #include<iostream> ...

  10. BZOJ1096:[ZJOI2007]仓库建设——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=1096 L公司有N个工厂,由高到底分布在一座山上.如图所示,工厂1在山顶,工厂N在山脚.由于这座山处于 ...