• 307. Range Sum Query


    问题:

    给定一个数组,实现方法:

    sumRange(i, j):求出第i个元素到第j个元素的和

    update(i, val):更新第i个元素的值为val

    Example:
    Given nums = [1, 3, 5]
    sumRange(0, 2) -> 9
    update(1, 2)
    sumRange(0, 2) -> 8
     
    
    Constraints:
    
    The array is only modifiable by the update function.
    You may assume the number of calls to update and sumRange function is distributed evenly.
    0 <= i <= j <= nums.length - 1
    

      

    解法:

    解法一:FenwickTree

    利用FenwickTree数据结构,构建一个数组的 “lowbit选择出的,连续元素和”(partsum) 底层数据结构。

    直接调用接口函数,

    • 更新某位置元素:update(i, diff)
    • 获取前缀和:PreSum(i)

    代码参考:

    class FenwickTree {
    public:
        FenwickTree(int n):partsum(n+1, 0){
            
        }
        void update(int i, int delta){
            while(i<partsum.size()){
                partsum[i]+=delta;
                i+=lowbit(i);
            }
        }
        int PreSum(int i){
            int sum=0;
            while(i>0){
                sum+=partsum[i];
                i-=lowbit(i);
            }
            return sum;
        }
    private:
        vector<int> partsum;
        int lowbit(int x){
            return x & (-x);
        }
    };
    
    class NumArray {
    public:
        NumArray(vector<int>& nums):num_(move(nums)), tree(nums.size()) {
            for(int i=0; i<num_.size(); i++){
                tree.update(i+1, num_[i]);
            }
        }
        
        void update(int i, int val) {
            int diff=val-num_[i];
            tree.update(i+1, diff);
            num_[i]=val;
        }
        
        int sumRange(int i, int j) {
            return tree.PreSum(j+1)-tree.PreSum(i);
        }
    private:
        FenwickTree tree;
        vector<int> num_;
    };
    
    /**
     * 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);
     */

    解法二:Segment Tree

    (线段树)

    定义TreeNode有以下变量:

    • int start_idx:该节点所代表的范围:起始
    • int end_idx:该节点所代表的范围:终止
    • int val:该节点的值(将来要合并的内容)
    • TreeNode *left:左子节点
    • TreeNode *right:右子节点

    关键方法皆使用 递归 方法,进行构建。

    1. 创建树:

    • Param:起始范围
    • return:树根节点
    • 递归退出条件:范围指向一个点。start==end,则直接由该点值val新建TreeNode返回。
    • 一般情况:由范围中点mid,对半分类讨论。最终左右合并到的共同父节点,需要新建TreeNode
    1     SegmentTreeNode* buildTree(int start, int end){
    2         if(start==end){
    3             return new SegmentTreeNode(start, end, num_[start], nullptr, nullptr);
    4         }
    5         int mid = start + (end-start)/2;
    6         SegmentTreeNode* left=buildTree(start, mid);
    7         SegmentTreeNode* right=buildTree(mid+1, end);
    8         return new SegmentTreeNode(start, end, left->val+right->val, left, right);
    9     }

    2. 更新树节点:

    • Param:当前树根节点:root,节点index:i,要更新到的值:val
    • return:void,不需要返回值
    • 递归退出条件:当前树根节点范围指向一个点:i。root->start==root->end==i,则直接更新该点值root->val,返回。
    • 一般情况:由范围中点mid,对半分类讨论,更新完毕。最终,需要更新左右共同父节点的值,root->val=left->val+right->val。
     1     void updateTree(SegmentTreeNode* root, int i, int val){
     2         if(root->start == i && root->end == i){
     3             root->val=val;
     4             return;
     5         }
     6         int mid = root->start + (root->end-root->start)/2;
     7         if(i<=mid) updateTree(root->left, i, val);
     8         else updateTree(root->right, i, val);
     9         root->val = root->left->val + root->right->val;
    10     }

    3. 求某段范围val合并值:

    • Param:当前树根节点:root,要求范围:i~j
    • return:合并值结果。
    • 递归退出条件:当前树根节点范围root->start~root->end刚好==i~j。root->start==i && root->end==j,则直接返回该点值root->val,返回。
    • 一般情况:由范围中点mid,对半分类讨论:
      • i~j全落在root->left:直接返回,递归调用本函数。root传入root->left
      • i~j全落在root->right:直接返回,递归调用本函数。root传入root->right
      • i~j分别落在root->left和root->right:返回 落在left的结果+落在right的结果:(当前树根节点:root->left, 范围:i~mid)+(当前树根节点:root->right, 范围:mid+1~j)
    1     int getSum(SegmentTreeNode* root, int i, int j){
    2         if(i==root->start && j==root->end){
    3             return root->val;
    4         }
    5         int mid = root->start + (root->end-root->start)/2;
    6         if(j<=mid) return getSum(root->left, i, j);
    7         else if(i>mid) return getSum(root->right, i, j);
    8         else return getSum(root->left, i, mid)+getSum(root->right, mid+1, j);
    9     }

    代码参考:

    class SegmentTreeNode {
    public:
        SegmentTreeNode(int start, int end, int val,
                        SegmentTreeNode *left,
                        SegmentTreeNode *right):
        start(start),
        end(end),
        val(val),
        left(left),
        right(right){}
        
        int start;
        int end;
        int val;
        SegmentTreeNode *left;
        SegmentTreeNode *right;
    };
    
    class NumArray {
    public:
        NumArray(vector<int>& nums):num_(move(nums)) {
            if(!num_.empty()) treeroot = buildTree(0, num_.size()-1);
        }
        
        void update(int i, int val) {
            updateTree(treeroot, i, val);
            num_[i]=val;
        }
        
        int sumRange(int i, int j) {
            return getSum(treeroot, i, j);
        }
    private:
        SegmentTreeNode* treeroot;
        vector<int> num_;
        
        SegmentTreeNode* buildTree(int start, int end){
            if(start==end){
                return new SegmentTreeNode(start, end, num_[start], nullptr, nullptr);
            }
            int mid = start + (end-start)/2;
            SegmentTreeNode* left=buildTree(start, mid);
            SegmentTreeNode* right=buildTree(mid+1, end);
            return new SegmentTreeNode(start, end, left->val+right->val, left, right);
        }
        void updateTree(SegmentTreeNode* root, int i, int val){
            if(root->start == i && root->end == i){
                root->val=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->val = root->left->val + root->right->val;
        }
        int getSum(SegmentTreeNode* root, int i, int j){
            if(i==root->start && j==root->end){
                return root->val;
            }
            int mid = root->start + (root->end-root->start)/2;
            if(j<=mid) return getSum(root->left, i, j);
            else if(i>mid) return getSum(root->right, i, j);
            else return getSum(root->left, i, mid)+getSum(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);
     */
  • 相关阅读:
    java面向对象
    java 继承
    .net的 http 请求 利用单例减少创建销毁的资源开销 提高利用率
    net core 3.1 swagger文档添加 不用xml配置
    vs code 配置 golang 环境
    .net 使用RabbitMQ demo
    Centos7 上安装配置 RabbitMQ
    C# .net 获取程序运行的路径的几种方法
    c# Windows服务应用程序的创建、安装和卸载
    XCOPY命令 windows 一个文件夹里的文件 快速移到另一个磁盘里
  • 原文地址:https://www.cnblogs.com/habibah-chang/p/13415734.html
Copyright © 2020-2023  润新知