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,即整个线段区间的长度。
![](https://img2020.cnblogs.com/blog/1248142/202003/1248142-20200331145328099-1841991116.png)
由于该题目有相当多的对数组修改的操作:
(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); } };