307. Range Sum Query - Mutable


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:

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

方法0:

思路:

沿用306的方法,在每次update之后都相应改变后面prefix的值。那么update这一步的时间复杂度就是O(n)。如何进行优化呢?

方法1: Binary Indexed Tree / Fenwick Tree

Tushar: https://www.youtube.com/watch?v=CWDQJGaN1gY
huahua: https://www.youtube.com/watch?v=WbafSgetDDk

Fenwick Tree is mainly designed for solving the single point update range sum problems. e.g. what’s the sum between i-th and j-th element while the values of the elements are mutable.

Complexity of Fenwick Tree

Init the tree (include building all prefix sums) takes O(nlogn)
Update the value of an element takes O(logn)
Query the range sum takes O(logn)
Space complexity: O(n)
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

思路:

标准BIT的代码实现 + 307:

// huahua:http://zxi.mytechroad.com/blog/sp/fenwick-tree-binary-indexed-tree-sp3/
class FenwickTree {
private:
	// low bit is to find the right most 1
	// used for i += lowbit(i) to find next
	// used for i -= lowbit(i) to find parent
    static inline int lowbit(int x) {
        return x & (-x);
    }
    vector<int> sums_;
public:
    FenwickTree(int n): sums_(n + 1, 0) {}
    
    void update(int i, int delta) {
        while (i < sums_.size()) {
            sums_[i] += delta;
            i += lowbit(i);
        }
    }
    
    int query(int i) const {
        int sum = 0;
        while (i > 0) {
            sum += sums_[i];
            i -= lowbit(i);
        }
        return sum;
    }
};

class NumArray {
private:
    vector<int> nums_;
    FenwickTree tree_;
public:  
    NumArray(vector<int> nums): nums_(std::move(nums)), tree_(nums_.size()) {
        for (int i = 0; i < nums_.size(); i++) {
            tree_.update(i + 1, nums_[i]);
        }
    }

    
    void update(int i, int val) {
        tree_.update(i + 1, val - nums_[i]);
        nums_[i] = val;
    }

    
    int sumRange(int i, int j) {
        return tree_.query(j + 1) - tree_.query(i);
    }
};

/**
 * 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);
 */

方法2: segment tree

官方题解:https://leetcode.com/problems/range-sum-query-mutable/solution/
专职跑龙套: https://www.jianshu.com/p/91f2c503e62f (brilliant introduction of segment tree)
huahua:https://www.youtube.com/watch?v=rYBtViWXYeI

Segment tree is a very flexible data structure, because it is used to solve numerous range query problems like finding minimum, maximum, sum, greatest common divisor, least common denominator in array in logarithmic time.

下面这个是标准版的segment tree实现。

struct SegmentTreeNode {
    int start;
    int end;
    int sum;
    SegmentTreeNode* left;
    SegmentTreeNode* right;
    
    SegmentTreeNode(int start, 
                    int end, 
                    int sum, 
                    SegmentTreeNode* left = nullptr,
                    SegmentTreeNode* right = nullptr):
        start(start),
        end(end),
        sum(sum){}  
};
class NumArray {
private: 
    SegmentTreeNode* root;
public:
     NumArray(vector<int>& nums) {
        int n = nums.size();
        root = buildTree(nums, 0, n - 1);
    }
    
    void update(int i, int val) {
        updateTree(root, i, val);    
    }
    
    
    int sumRange(int i, int j) {
        return queryTree(root, i, j);
    }
    
    SegmentTreeNode* buildTree(vector<int> & nums, int start, int end) {
        if (start > end) return nullptr;
        SegmentTreeNode* root = new SegmentTreeNode(start, end, 0);
        if (start == end) {
            root -> sum = nums[start];
            return root;
        }
        
        int mid = start + (end - start) / 2;
        root -> left = buildTree(nums, start, mid);
        root -> right = buildTree(nums, mid + 1, end);
        root -> sum = root -> left -> sum + root -> right -> sum;
        return root;
    }
    
    int updateTree(SegmentTreeNode* root, int i, int val){
        if (root == nullptr) return 0;
        int diff;
        if (root -> start == i && root -> end == i) {
            diff = val - root -> sum;
            root -> sum = val;
            return diff;
        }
        
        int mid = root -> start + (root -> end - root -> start) / 2;
        if (i <= mid) {
            diff = updateTree(root -> left, i, val);
        }
        else {
            diff = updateTree(root -> right, i, val);
        }
        root -> sum = root -> sum + diff;
        return diff;
    }
    
    int queryTree(SegmentTreeNode * root, int i, int j) {
        if (!root) return 0;
        if (root -> start == i && root -> end ==j) return root -> sum;
        int mid = root -> start + (root -> end - root -> start) / 2;
        if (j <= mid) {
            return queryTree(root -> left, i, j);
        }
        else if (i > mid) {
            return queryTree(root -> right, i, j);
        }
        else return queryTree(root -> left, i, mid) + queryTree(root -> right, mid + 1, j);
    }
};

/**
 * 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);
 */
// huahua: https://www.youtube.com/watch?v=rYBtViWXYeI
class SegmentTreeNode {
public:
    int start;
    int end;
    int sum;
    SegmentTreeNode* left;
    SegmentTreeNode* right;
    
    SegmentTreeNode(int start, int end, int sum,
                   SegmentTreeNode* left = nullptr,
                   SegmentTreeNode* right = nullptr):
        start(start),
        end(end),
        sum(sum),
        left(left),
        right(right){}
    SegmentTreeNode(const SegmentTreeNode &) = delete;
    SegmentTreeNode & operator = (const SegmentTreeNode &) = delete;
    ~SegmentTreeNode() {
        delete left;
        delete right;
        left = right = nullptr;
    }
};

class NumArray {
private:
    vector<int> nums_;
    std::unique_ptr<SegmentTreeNode> root_;
    
    SegmentTreeNode* buildTree(int start, int end) {
        if (start == end) {
            return new SegmentTreeNode(start, end, nums_[start]);
        }
        int mid = start + (end - start) / 2;
        auto left = buildTree(start, mid);
        auto right = buildTree(mid + 1, end);
        return new SegmentTreeNode(start, end, left -> sum + right -> sum, left, right);
    }

    void updateTree(SegmentTreeNode* root, int i, int val) {
        if (root -> start == i && root -> end == i) {
            root -> sum = val;
            return;
        }
        int mid = root -> start + (root -> end - root -> start) / 2;
        if (i <= mid) {
            updateTree(root -> left, i, val);
        }
        else {
            updateTree(root -> right, i, val);
        }
        root -> sum = root -> left -> sum + root -> right -> sum;
    }
    
    int sumRange(SegmentTreeNode* root, int i, int j) {
        if (i == root -> start && i == root -> end) {
            return root -> sum;
        }
        
        int mid = root -> start + (root -> end - root -> start) / 2;
        if (j <= mid) {
            return sumRange(root -> left, i, j);
        } else if (i > mid) {
            return sumRange(root -> right, i, j);
        } else {
            return sumRange(root -> left, i, mid) + sumRange(root -> right, mid + 1, j);
        }
    }
public:    
    NumArray(vector<int>& nums) {
        nums_.swap(nums);
        if (!nums_.empty()){
            root_.reset(buildTree(0, nums_.size() - 1));
        }
    }
    
    void update(int i, int val) {
        updateTree(root_.get(), i, val);
    }
    
    int sumRange(int i, int j) {
        return sumRange(root_.get(), i, j);
    }
};

/**
 * 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);
 */

方法3: segment tree (implemented with array)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值