《307. 区域和检索

leetcode 307. 区域和检索 - 数组可修改

给定一个整数数组  nums,求出数组从索引 i 到 j  (i ≤ j) 范围内元素的总和,包含 i,  j 两点。

update(i, val) 函数可以通过将下标为 i 的数值更新为 val,从而对数列进行修改。

示例:

Given nums = [1, 3, 5]

sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/range-sum-query-mutable
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

线段树在百度百科上的定义:

线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。
 
 
由于该题目有相当多的对数组修改的操作:
(1)如果直接在原有的数据结构,即数组上进行修改,每一次修改将花费O(1), 每一次的区间和输出都将花费O(n)的时间复杂度
(2)但当我们建立起一个区间和的线段树,每次修改和输出都将花费O(logn)的时间复杂度
 
用二叉树结构来组织线段树:
class NumArray {
    TreeNode* root;
    vector<int> num;
    int n;
public:

    NumArray(vector<int>& nums) {
        n=nums.size()-1;
        num=nums;
        if(n>=0)
            root=creatTree(nums,0,n); //针对初始化时数组为[[]]
    }

    TreeNode* creatTree(vector<int>& nums,int x, int y )
    {
        if(x==y)
        {
            TreeNode* tmp=new TreeNode(nums[x]);
            return tmp;
        }
        else
        {
            int sum=0;
            for(int i=x;i<=y;i++)
            {
                sum+=nums[i];
            }
            TreeNode* tmp = new TreeNode(sum);
            int mid=(x+y)/2;
            tmp->left=creatTree(nums,x,mid);
            tmp->right=creatTree(nums,mid+1,y);
            return tmp;
        }
    }
    
    void update(int i, int val) {
        TreeNode* r=root;
        int leftBorder=0,rightBorder=n;
        while(r)
        {
            r->val=r->val-num[i]+val;
            int mid = (leftBorder+rightBorder)/2;
            if(i<=mid)
            {
                r=r->left;
                rightBorder=mid;
            }
            else
            {
                r=r->right;
                leftBorder=mid+1;
            }
        }
        num[i]=val;
    }
    int sum(TreeNode* r, int i, int j, int leftBorder, int rightBorder)
    {
        if(r==NULL)return 0;
        if(i==leftBorder&&j==rightBorder)return r->val;
        int mid = (leftBorder+rightBorder)/2;
        if(i<=mid&&j>mid)
        {
            return sum(r->left, i, mid, leftBorder, mid)+sum(r->right, mid+1, j, mid+1, rightBorder);
        }
        else if(i<=mid)
        {
            return sum(r->left, i, j, leftBorder, mid);
        }
        else if(i>mid)
        {
            return sum(r->right, i, j, mid+1, rightBorder);
        }
        return INT_MAX;
    }
    int sumRange(int i, int j) {
        TreeNode* r=root;
        return sum(r, i ,j, 0, n);
    }
};

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

用树状数组来组织线段树:

class NumArray {
    vector<int> segmTree;;
    int n;
public:
    NumArray(vector<int>& nums) {
        n=nums.size()-1;
        if(n >= 0)
            buildSegmTree(nums,0,0,n);
    }
   //自底向上建树
    void buildSegmTree(vector<int>& nums,int cur, int begin, int end )
    {
        while(cur >= segmTree.size())segmTree.push_back(0);
        if(begin == end)
        {
            segmTree[cur] = nums[begin];
        }
        else
        {
            int mid = (begin + end) / 2;
            int left = cur * 2 + 1;    //左子区间下标
            int right = cur * 2 + 2;    //右子区间下标
            buildSegmTree(nums,left,begin,mid);
            buildSegmTree(nums,right,mid+1,end);
            segmTree[cur] = segmTree[left] + segmTree[right];
        }
    }
  //自底向上更新
void updateSegmTree(int cur, int begin, int end, int i, int val) { if(begin > end)return; if(begin == end) { segmTree[cur]=val; return; } int mid = (begin + end) / 2; int left = 2 * cur + 1; int right = 2 * cur +2; if(i <= mid && i >= begin) { updateSegmTree(left, begin, mid, i, val); } else { updateSegmTree(right, mid+1, end, i, val); } segmTree[cur] = segmTree[left] + segmTree[right]; } void update(int i, int val) { updateSegmTree(0, 0, n, i, val); }
  //自顶向下找区间
int sum( int cur, int i, int j, int begin, int end) { if(i > end || j < begin)return 0; if(i <= begin && j >= end) return segmTree[cur]; int mid = (begin + end) / 2; int left = cur*2 + 1; int right = cur*2 + 2; return sum(left, i, j, begin, mid)+sum(right, i, j, mid+1, end); } int sumRange(int i, int j) { return sum(0, i ,j, 0, n); } };
原文地址:https://www.cnblogs.com/Dancing-Fairy/p/12605309.html