LeetCode #307. Range Sum Query - Mutable 数组 线段树

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

Note:

  • The array is only modifiable by the update function.
  • You may assume the number of calls to update and sumRange function is distributed evenly.



思路


解法一

暴力。

时间复杂度:

  • 构造数组:O(n)
  • update操作:O(1)
  • sumRange操作:O(n)

空间复杂度:O(n)

耗时 216 ms, faster than 21%, Memory 16.6 MB

class NumArray {
public:
    NumArray(const vector<int> &numbers) : nums(numbers) {}

    void update(int i, int val) {
        nums[i] = val;
    }

    int sumRange(int i, int j) {
        int sum = 0;
        while (i <= j) {
            sum += nums[i];
            ++i;
        }

        return sum;
    }

private:
    vector<int> nums;
};

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray* obj = new NumArray(nums);
 * obj->update(i,val);
 * int param_2 = obj->sumRange(i,j);
 */



解法二

线段树,本质是使用二分法拆分可变区间。

线段树是一颗可以携带额外信息的满二叉树,比如携带子树的结点和,或者最大值,最小值等等,这样,当某个结点的值发生变化时,只需要更新一下其所有祖先结点的信息,而并不需要更新整棵树,这样就极大的提高了效率。

这是我第一次实现线段树,比较吃力,解题思路参照的是这个博客:[LeetCode] 307. Range Sum Query - Mutable 区域和检索 - 可变

时间复杂度:

  • 建树:O(n)
  • update操作:O(lgn)
  • sumRange操作:O(lgn)

空间复杂度:O(n)

耗时 48 ms, faster than 71.15%, Memory 16.7 MB

// nums      1  3  5  7
// tree      0 16  4 12  1  3  5  7
//   i       0  1  2  3  4  5  6  7
//
//           1  3  5  7  9
//           0 25 17  8 16  1  3  5  7  9
//           0  1  2  3  4  5  6  7  8  9

class NumArray {
public:
    NumArray(const vector<int> &nums) {
        n = nums.size();
        tree.resize(2 * nums.size(), 0);
        build_seg_tree(nums);
    }

    void build_seg_tree(const vector<int> &nums) {
        for (int i = 0; i < nums.size(); ++i) {
            tree[i + n] = nums[i];
        }

        for (int i = n - 1; i > 0; --i) {
            tree[i] = tree[2 * i] + tree[2 * i + 1];
        }
    }

    void update(int i, int val) {
        int idx = n + i;  // position of nums[i] in tree
        if (tree[idx] == val) return;

        tree[idx] = val;

        // update value of parent node
        while (idx > 0 && idx / 2 != 0) {
            int another_son_idx = idx ^ 1;
            int parent_idx = idx / 2;
            tree[parent_idx] = tree[idx] + tree[another_son_idx];

            idx /= 2;
        }
    }

    int sumRange(int i, int j) {
        i += n;
        j += n;
        int sum = 0;

        while (i <= j) {
            if ((i & 1) == 1) {  // element is right son of subtree or parent node
                sum += tree[i];
                ++i;  // move to a subtree on the right
            }

            if ((j & 1) == 0) {  // element is left son of subtree or parent node
                sum += tree[j];
                --j;  // move to a subtree on the left
            }

            // move to the parent node
            i /= 2;
            j /= 2;
        }

        return sum;
    }

private:
    int n;  // numbers of element in nums
    vector<int> tree;
};


/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray* obj = new NumArray(nums);
 * obj->update(i,val);
 * int param_2 = obj->sumRange(i,j);
 */



参考




posted @ 2020-04-23 17:49  bw98  阅读(174)  评论(0编辑  收藏  举报