题目:

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.

链接: http://leetcode.com/problems/range-sum-query-mutable/

题解:

这应该算是Range Query的经典题目之一了。也是通过这道题我第一次接触到了Segment Tree,也对Fenwick Tree有了一点了解。下面是用Segment Tree来做的。 Segment Tree线段树每一个节点都是一段线段,有start和end,然后还可以有其他的值,比如区间和sum,区间最大值max,区间最小值min。我们可以用自底向上构建二叉树的方式构建Segment Tree,这个过程也有点类似于Bottom-up的merge sort,思想也是Divide and Conquer。完毕之后就可以在O(logn)的时间update,或者得到range Sum。其实更好的方法是使用Fenwick Tree, Fenwick Tree(Binary Indexed Tree)在处理Range Query真的是一绝,构造简练,原理也精妙,还可以扩展到多维,一定要好好学一学。

Time Complexity - O(n) build, O(logn) update, O(logn) rangeSum,  Space Complexity - O(n)

public class NumArray {
private class SegmentTreeNode {
public int start;
public int end;
public int sum;
public SegmentTreeNode left, right;
public SegmentTreeNode(int start, int end) {
this.start = start;
this.end = end;
this.sum = 0;
}
} private SegmentTreeNode root; public NumArray(int[] nums) {
this.root = buildTree(nums, 0, nums.length - 1);
} public void update(int i, int val) {
update(root, i, val);
} private void update(SegmentTreeNode node, int position, int val) {
if(node.start == position && node.end == position) {
node.sum = val;
return;
}
int mid = node.start + (node.end - node.start) / 2;
if(position <= mid) {
update(node.left, position, val);
} else {
update(node.right, position, val);
}
node.sum = node.left.sum + node.right.sum;
} public int sumRange(int i, int j) {
return sumRange(root, i, j);
} private int sumRange(SegmentTreeNode node, int lo, int hi) {
if(node.start == lo && node.end == hi) {
return node.sum;
}
int mid = node.start + (node.end - node.start) / 2;
if(hi <= mid) {
return sumRange(node.left, lo, hi);
} else if (lo > mid) {
return sumRange(node.right, lo, hi);
} else {
return sumRange(node.left, lo, mid) + sumRange(node.right, mid + 1, hi);
}
} private SegmentTreeNode buildTree(int[] nums, int lo, int hi) {
if(lo > hi) {
return null;
} else {
SegmentTreeNode node = new SegmentTreeNode(lo, hi);
if(lo == hi) {
node.sum = nums[lo];
} else {
int mid = lo + (hi - lo) / 2;
node.left = buildTree(nums, lo, mid);
node.right = buildTree(nums, mid + 1, hi);
node.sum = node.left.sum + node.right.sum;
}
return node;
}
}
} // Your NumArray object will be instantiated and called as such:
// NumArray numArray = new NumArray(nums);
// numArray.sumRange(0, 1);
// numArray.update(1, 10);
// numArray.sumRange(1, 2);

Fenwick Tree:  (Binary Indexed Tree) (树状数组)

很有意思的构建,以数组nums = {1, 2, 3, 4, 5, 6, 7, 8}为例,这个数组长度为8。 跟dynamic programming的预处理很像,我们先建立一个长度为nums.length + 1 = 9的数组BIT。接下来遍历数组nums,对BIT数组进行update(i + 1, nums[i])。这里BIT数组每个值BIT[i]代表nums数组里在i之前的部分元素和。原理是像自然数可以被表示为2n的和一样,把nums数组里到0到i的sum表示成2n的和,从而导致update和rangeSum都可以用O(logn)的时间求出来。这里构建的时候可以有几种写法,主要就是利用当前i的least significante 1来确定到底BIT[i]要保存多少原数组的值。这里借用algorithmist的原话"Every index in the cumulative sum array, say i, is responsible for the cumulative sum from the index i to (i - (1<<r) + 1)。" 构建过程中可以用 (i & -i)来找到least significate 1,之后来进行i = i + (i & -i)来尝试从小到大计算下一个BIT数组中被影响的元素。 而rangeSum的时候则使用i = i - (i & -i)来从大到小查找从0到i - 1的sum。

构建过程 - update, 给定数组nums = {1,2, 3, 4, 5, 6, 7, 8}

BIT[0] = 0

BIT[1] = nums[0] = 1 = 1

BIT[2] = nums[0] + nums[1] = 1 + 2 = 3

BIT[3] = nums[2] = 3 = 3

BIT[4] = nums[0] + nums[1] + nums[2] + nums[3] = 1+ 2 + 3 + 4 = 10

BIT[5] = nums[4] = 5 = 5

BIT[6] = nums[4] + nums[5] = 5 + 6 = 11

BIT[7] = nums[6] = 7 = 7

BIT[8] = nums[0] + nums[1] + nums[2] + nums[3] + nums[4] + nums[5] + nums[6] + nums[7] = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 = 36

求Sum过程, 通过 sum = BIT[i + 1];   i = i - (i & -i);  从大到小迭代来计算。

sum(0) = BIT[1]

sum(1) = BIT[2]

sum(2) = BIT[3] + BIT[2]

sum(3) = BIT[4]

sum(4) = BIT[5] + BIT[4]

sum(5) = BIT[6] + BIT[4]

sum(6) = BIT[7] + BIT[6] + BIT[4]

sum(7) = BIT[8]

得到sum(i)以后就可以相减来计算range sum了。

Time Complexity - O(nlogn) build,  O(logn) update, O(logn) rangeSum,  Space Complexity - O(n)

public class NumArray {
private int BIT[]; // Binary Indexed Tree = Fenwick Tree
private int[] nums; public NumArray(int[] nums) {
BIT = new int[nums.length + 1];
for(int i = 0; i < nums.length; i++) {
init(i + 1, nums[i]);
}
this.nums = nums;
} private void init(int i, int val) {
while(i < BIT.length) {
BIT[i] += val;
i = i + (i & -i);
}
} public void update(int i, int val) {
int delta = val - nums[i];
nums[i] = val;
init(i + 1, delta);
} public int sumRange(int i, int j) {
return getSum(j + 1) - getSum(i);
} private int getSum(int i) {
int sum = 0;
while(i > 0) {
sum += BIT[i];
i = i - (i & -i);
}
return sum;
}
}

题外话: 今天去NYU图书馆自习,正值考期,人山人海的。我带了电脑却忘记带插座,无奈只能用Java 在线的编译器https://www.compilejava.net/, 不过这个真的还挺好用,除了不能debug,其他都可以,nice。晚上吃了Hakata Tonton,4个人大概人均$50+,并没有想象的那么好吃,以后还是要攒机会去日本玩。 刷题群里大家对Heapify有了热烈的讨论,我自己认为Heapify主要有两种,Bottom-up (swim)和Top-down(sink)。也要复习一下Priority Queue的implementation。要多看Sedgewick的课件和sample code才行。 在GitHub发现有个人叫indy256,实现了好多好多高级数据结构,有2d-fenwick tree,以及O(n)的Suffix Tree。大牛和普通人的距离真的好遥远,我还是继续努力。

二刷:

暂时只用了segment tree。  Fenwick Tree以后再理解。

Java:

Segment Tree:

public class NumArray {
private SegmentTreeNode root;
private int[] nums; public NumArray(int[] nums) {
this.nums = nums;
this.root = buildTree(0, nums.length - 1);
} void update(int i, int val) {
update(root, i, val);
} private void update(SegmentTreeNode node, int pos, int val) {
if (node == null) return;
if (node.start == pos && node.end == pos) {
node.val = val;
nums[pos] = val;
return;
}
int mid = node.start + (node.end - node.start) / 2;
if (pos <= mid) {
update(node.left, pos, val);
} else {
update(node.right, pos, val);
}
node.val = node.left.val + node.right.val;
} public int sumRange(int i, int j) {
return sumRange(root, i, j);
} private int sumRange(SegmentTreeNode node, int lo, int hi) {
if (lo > hi) return 0;
if (node.start == lo && node.end == hi) return node.val;
int mid = node.start + (node.end - node.start) / 2;
if (hi <= mid) {
return sumRange(node.left, lo, hi);
} else if (lo > mid) {
return sumRange(node.right, lo, hi);
} else {
return sumRange(node.left, lo, mid) + sumRange(node.right, mid + 1, hi);
}
} private SegmentTreeNode buildTree(int lo, int hi) {
if (lo > hi) return null;
SegmentTreeNode node = new SegmentTreeNode(lo, hi);
if (lo == hi) {
node.val = nums[lo];
} else {
int mid = lo + (hi - lo) / 2;
node.left = buildTree(lo, mid);
node.right = buildTree(mid + 1, hi);
node.val = node.left.val + node.right.val;
}
return node;
} private class SegmentTreeNode {
int start;
int end;
int val;
SegmentTreeNode left, right; public SegmentTreeNode(int start, int end) {
this.start = start;
this.end = end;
this.val = 0;
}
}
} // Your NumArray object will be instantiated and called as such:
// NumArray numArray = new NumArray(nums);
// numArray.sumRange(0, 1);
// numArray.update(1, 10);
// numArray.sumRange(1, 2);

Reference:

https://www.compilejava.net/
http://algs4.cs.princeton.edu/93intersection/IntervalST.java.html
https://leetcode.com/discuss/70202/17-ms-java-solution-with-segment-tree
http://www.geeksforgeeks.org/segment-tree-set-1-sum-of-given-range/
http://algs4.cs.princeton.edu/99misc/SegmentTree.java.html/
https://leetcode.com/discuss/70272/solution-using-buckets-updating-for-query-the-worst-case-fast
https://leetcode.com/discuss/74222/java-using-binary-indexed-tree-with-clear-explanation
https://leetcode.com/discuss/70278/simple-recursive-java-solution
http://algs4.cs.princeton.edu/99misc/FenwickTree.java.html
https://web.stanford.edu/class/cs97si/03-data-structures.pdf
https://en.wikipedia.org/wiki/Fenwick_tree
http://algs4.cs.princeton.edu/99misc/FenwickTree.java.html
http://cs.nyu.edu/courses/spring14/CSCI-UA.0480-004/Lecture4.pdf
https://www.topcoder.com/community/data-science/data-science-tutorials/
http://www.algorithmist.com/index.php/Fenwick_tree
https://leetcode.com/discuss/70273/java-7ms-binary-index-tree-solution
https://leetcode.com/discuss/72658/java-solution-with-binary-indexed-tree-beats-81-95%25
https://leetcode.com/discuss/74222/java-using-binary-indexed-tree-with-clear-explanation
https://leetcode.com/discuss/70311/11-ms-java-binary-tree
https://leetcode.com/discuss/70191/share-my-c-solution-1700ms-using-tree-array
https://leetcode.com/discuss/70293/java-binary-indexed-tree

https://sites.google.com/site/indy256/algo_cpp/fenwick_tree

https://sites.google.com/site/indy256/algo/fenwick_tree_2d

http://www.cnblogs.com/grandyang/p/4985506.html

http://citeseerx.ist.psu.edu/viewdoc/download;jsessionid=A4ADC19B8D7E3A202944A808F5840D21?doi=10.1.1.14.8917&rep=rep1&type=pdf

http://arxiv.org/pdf/1311.6093v5.pdf

https://leetcode.com/discuss/72658/java-solution-with-binary-indexed-tree-beats-81-95%25

307. Range Sum Query - Mutable的更多相关文章

  1. 【刷题-LeetCode】307. Range Sum Query - Mutable

    Range Sum Query - Mutable Given an integer array nums, find the sum of the elements between indices ...

  2. [LeetCode] 307. Range Sum Query - Mutable 区域和检索 - 可变

    Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...

  3. leetcode@ [307] Range Sum Query - Mutable / 线段树模板

    Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...

  4. [LeetCode] 307. Range Sum Query - Mutable 解题思路

    Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...

  5. LeetCode - 307. Range Sum Query - Mutable

    Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...

  6. 307. Range Sum Query - Mutable查询求和的范围(可变)

    [抄题]: Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inc ...

  7. leetcode 307. Range Sum Query - Mutable(树状数组)

    Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive ...

  8. 【leetcode】307. Range Sum Query - Mutable

    题目如下: 解题思路:就三个字-线段树.这个题目是线段树用法最经典的场景. 代码如下: class NumArray(object): def __init__(self, nums): " ...

  9. [Leetcode Week16]Range Sum Query - Mutable

    Range Sum Query - Mutable 题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/range-sum-query-mutable/de ...

随机推荐

  1. c++ goto的使用

    c++ goto 语句的使用 1.定义一个类似标签的东西lable 2.使用goto关键字,跳转到lable, goto lable #include <iostream> #includ ...

  2. SQL SERVER完整、差异和事务日志备份及还原(脚本和GUI实现) [原创]

    一.完整备份.差异备份和事务日志备份的脚本 --完整备份数据库 BACKUP DATABASE Test_Bak TO DISK = 'E:\20150609_75\bak\Test_bak_full ...

  3. “我爱淘”冲刺阶段Scrum站立会议6

    完成任务: 对大部分的布局已经做好了布置. 计划任务: 实现数据库的链接,将页面功能完善. 遇到问题: 使用webservice对数据的提取,用数据库将书的信息展示软件中.

  4. ELK kibana查询与过滤(17th)

    在kibana中,可通过搜索查询过滤事务或者在visualization界面点击元素过滤. 创建查询 在Discover界面的搜索栏输入要查询的字段.查询语法是基于Lucene的查询语法.允许布尔运算 ...

  5. Introduction to Financial Management

    Recently,i am learning some useful things about financial management by reading <Essentials of Co ...

  6. 网络服务器带宽Mbps、Mb/s、MB/s有什么区别?10M、100M到底是什么概念?

    网络服务器带宽Mbps.Mb/s.MB/s有什么区别?我们经常听到IDC提供的服务器接入带宽是10M独享,或者100M独享,100M共享之类的数据.这的10M.100M到底是什么概念呢? 工具/原料 ...

  7. CSS中的视觉格式化模型

    视觉格式化模型 1. 简介 在视觉格式化模型中,文档树中的每个元素都将会根据盒模型产生零到多个盒子.这些盒子的布局由如下因素决定: 盒子的尺寸和类型 定位策略(正常文档流,浮动或者绝对定位) 和文档树 ...

  8. [转]日期格式化(yyyy-MM-dd)中,为什么 M 多大写?

    最近犯了个可傻逼的错误,格式化年月日的时候不小心将yyyy-MM-dd写成YYYY-MM-dd,导致格式化结果中年不正确. 看看知乎上的说法 问题: http://www.zhihu.com/ques ...

  9. jQuery1.9.1--结构及$方法的工作原理源码分析

    jQuery的$方法使用起来非常的多样式,接口实在太灵活了,有点违反设计模式的原则职责单一.但是用户却非常喜欢这种方式,因为不用记那么多名称,我只要记住一个$就可以实现许多功能,这个$简直就像个万能的 ...

  10. ES6中的高阶函数:如同 a => b => c 一样简单

    作者:Sequoia McDowell 2016年01月16日 ES6来啦!随着越来越多的代码库和思潮引领者开始在他们的代码中使用ES6,以往被认为是"仅需了解"的ES6特性变成了 ...