Description:
Given an integer array nums, return thenumber of range sums that lie in [lower, upper] inclusive.
Range sum S(i, j) is defined as the sum ofthe elements in nums between indices i and j (i ≤j), inclusive.
Note:
A naive algorithm of O(n2) is trivial. YouMUST do better than that.
Example:
Given nums = [-2, 5, -1], lower = -2,upper = 2,
Return 3.
The three ranges are : [0, 0], [2, 2], [0,2] and their respective sums are: -2, -1, 2.
Tags:Divide and Conquer, Binary Search Tree
题意理解:
这道题在说什么?
给定一个整数的数组 int[]nums, 给出一个范围区间[lower,upper].
找出数组中这一样一些子数组,他们的元素和在给定的区间内,并求出有多少个这样的子数组。如:int[j-i] sub_nums = [nums[i], nums[i+1],…, nums[j]] (i<=j),并且lower<=sum(for item in sub_nums) <=upper, 找出有多少个这样的sub_sums.
思路探讨:
1. 枚举:简单粗暴如我,最先想到的就是将所有的情况遍历一遍。
比如:给定 int[n] nums, 遍历
nums[0], nums[0]+nums[1],nums[0]+nums[1]+nums[2],…….., nums[0] +nums[1]+nums[2]+…+nums[n-1];
nums[1], nums[1]+nums[2],… nums[1]+nums[2]+…+nums[n-1];
……………………
nums[n-1]
但是这种方法运算复杂度较高,O(n^2),不符合题目要求。故放弃。
第一种方法虽然在本题中不能用,却依然给我们提供一些解题思路。
如果我们定义新数组int[n]sum 来表示数组nums前n项元素和的话, 即:
sum[i] = num[0]+…. +nums[i-1].
那么我们第一种思路的遍历就可以写成:
sum[0], sum[1], sum[2], ….sum[n];
sum[1]-sum[0], sum[2]-sum[0],…sum[n]-sum[0];
sum[2]-sum[1], sum[3]-sum[1], … sum[n]- sum[1];
…………………..
sum[n]-sum[n-1]
这样我们只需要求出在区间[lower,upper]中的sum[j]-sum[i]个数即可, 即:
判断是否有lower<= sum[j]- sum[i] <= upper.
而对于特定的sum[j], 若想满足上述条件, 则需要求出对应的sum[i],满足:
sum[j]-upper <=sum[i]<=sum[j]-lower.
即:对于特定sum[j],我们只需求出在区间[sum[j]-upper,sum[j]-lower]中的sum[i]的个数,我们就可求出在区间[lower,upper]中的sum[j]-sum[i]个数。
那么这道题就转换为了求数组sum中值在特定区间[sum[j]-upper,sum[j]-lower]中的元素个数问题。
值得注意的是,这里的特定区间是随着sum[j]值的变化而变化的,我想这就是这道题用Divideand Conquer作为标签之一的原因吧。
到这里我们的整体解题思路就出来了,但是在实现过程中我们需要注意,求数组在特定区间中的元素通常情况下也需要遍历数组,这样复杂度还是会很高。我们就需要提前采取手段使sum数组成为有序的。
2和3便提供了两种方法。
2. pythonbisect:
关于pythonbisect介绍即用法请参考:
http://www.cnblogs.com/skydesign/archive/2011/09/02/2163592.html
https://docs.python.org/2/library/bisect.html
参考代码[1]如下:
import bisect
class Solution(object):
def countRangeSum(self, nums, lower, upper):
"""
:type nums: List[int]
:type lower: int
:type upper: int
:rtype: int
"""
sum_list, cur_sum, count = [],0,0
for num in nums:
cur_sum+=num #get the sum of all numbers before current number
if lower <=cur_sum<=upper:
count +=1
if not sum_list:
sum_list.append(cur_sum)
continue
low = cur_sum -lower # sum[j]-sum[i] <upper -> sum[j]-upper < sum[i]
hi = cur_sum - upper # sum[j]-sum[i] >lower -> sum[j]-lower > sum[i]
idx_lo = bisect.bisect_right(sum_list,low)
idx_hi = bisect.bisect_left(sum_list,hi)
count +=abs(idx_lo-idx_hi)
bisect.insort(sum_list,cur_sum) #insert current sum and sort the sum list
return count
3. BinarySearch Tree:
Java中似乎没有与bisect相对应的排序模块?
不过不要着急,TagBinary Search Tree已经为我们泄露了天机,我们可以通过二叉搜索树来实现我们的小目标(微笑)
参考代码[2]如下:public class countOfRangeSum_327 {
private class TreeNode{
long val = 0;
int count = 1;
int leftSize = 0;
int rightSize = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(long v){
this.val = v;
this.count = 1;
this.leftSize =0;
this.rightSize = 0;
}
}
private TreeNode insert(TreeNode root, long val){
if(root == null){
return new TreeNode(val);
}else if(root.val == val){
root.count++;
}else if(val < root.val){
root.leftSize++;
root.left = insert(root.left,val);
}else if(val>root.val){
root.rightSize++;
root.right = insert(root.right,val);
}
return root;
}
private int countSmaller(TreeNode root, long val){
if(root==null){
return 0;
}else if(root.val == val){
return root.leftSize;
}else if(root.val > val){
return countSmaller(root.left,val);
}else{
return root.leftSize + root.count+countSmaller(root.right,val);
}
}
private int countLarger(TreeNode root, long val){
if(root == null){
return 0;
}else if(root.val == val){
return root.rightSize;
}else if(root.val < val){
return countLarger(root.right,val);
}else {
return countLarger(root.left,val)+root.count+root.rightSize;
}
}
private int rangeSize(TreeNode root, long lower, long upper){
int total = root.count + root.leftSize+root.rightSize;
int smaller = countSmaller(root,lower);
int larger = countLarger(root, upper);
return total - smaller - larger;
}
public int countRangeSum(int[] nums, int lower, int upper){
if(nums.length == 0){
return 0;
}
long[] sums = new long[nums.length+1];
for(int i = 0; i < nums.length; i++){
sums[i+1]=sums[i]+nums[i];
}
TreeNode root = new TreeNode(sums[0]);
int output = 0;
for(int i =1; i < sums.length; i++){
output+=rangeSize(root, sums[i]-upper, sums[i]- lower);
insert(root, sums[i]);
}
return output;
}
}
相似问题:
总结:
这道题刚看到的时候真是丈二和尚摸不着头脑,这时需要耐心地读懂题意,然后不妨从最容易想到的方法入手,相信聪明的你一定可以顺藤摸瓜找出思路的。
PS:反正说了这么多,让我自己做还是不会(微笑)。
Reference:
[2]. https://discuss.leetcode.com/topic/34107/java-bst-solution-averagely-o-nlogn/2