原题网址:https://leetcode.com/problems/count-of-range-sum/
Given an integer array nums
, return the number of range sums that lie in [lower, upper]
inclusive.
Range sum S(i, j)
is defined as the sum of the elements in nums
between indices i
and j
(i
≤ j
), inclusive.
Note:
A naive algorithm of O(n2) is trivial. You MUST 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
.
方法一:使用TreeMap来保存每个前缀和的计数。
public class Solution {
public int countRangeSum(int[] nums, int lower, int upper) {
if (nums == null || nums.length==0) return 0;
long[] sums = new long[nums.length];
sums[0] = nums[0];
for(int i=1; i<nums.length; i++) sums[i] = sums[i-1] + nums[i];
int total = 0;
TreeMap<Long, Integer> treemap = new TreeMap<>();
for(int i=0; i<nums.length; i++) {
if (lower <= sums[i] && sums[i] <= upper) total ++;
for(Integer count: treemap.subMap(sums[i]-upper, true, sums[i]-lower, true).values()) {
total += count;
}
Integer count = treemap.get(sums[i]);
if (count == null) count = 1; else count ++;
treemap.put(sums[i], count);
}
return total;
}
}
方法二:使用二叉搜索树(相当于方法一的自己实现)
public class Solution {
Node root;
private int sub(Node node, long lower, long upper) {
// System.out.printf("sub=%s, lower=%d, upper=%d\n", node==null?"null":Long.toString(node.val), lower, upper);
if (node == null) return 0;
if (node.val < lower) return sub(node.right, lower, upper);
if (upper < node.val) return sub(node.left, lower, upper);
return node.count + sub(node.left, lower, node.val-1) + sub(node.right, node.val+1, upper);
}
private void insert(Node node, long val) {
if (node.val < val) {
if (node.right == null) node.right = new Node(val);
insert(node.right, val);
} else if (val < node.val) {
if (node.left == null) node.left = new Node(val);
insert(node.left, val);
} else {
node.count ++;
}
}
public int countRangeSum(int[] nums, int lower, int upper) {
if (nums == null || nums.length == 0) return 0;
long[] sums = new long[nums.length];
sums[0] = nums[0];
for(int i=1; i<nums.length; i++) sums[i] = sums[i-1] + nums[i];
root = new Node(nums[0]);
int count = 0;
for(int i=0; i<nums.length; i++) {
// System.out.printf("i=%d, sums[i]=%d\n", i, sums[i]);
if (lower <= sums[i] && sums[i] <= upper) {
count ++;
// System.out.printf("1. count=%d\n", count);
}
count += sub(root, sums[i]-upper, sums[i]-lower);
// System.out.printf("2. count=%d\n", count);
insert(root, sums[i]);
}
return count;
}
}
class Node {
long val;
int count;
Node left, right;
Node(long val) {
this.val = val;
}
}
方法三:改进的二叉搜索树(保存左子树节点数量)
public class Solution {
private Node build(long[] nums, int from, int to) {
if (from>to) return null;
int m = (from+to)/2;
Node node = new Node(nums[m]);
if (from<m) node.left = build(nums, from, m-1);
if (m<to) node.right = build(nums, m+1, to);
return node;
}
private void update(Node node, long val) {
if (node == null) return;
if (val < node.val) {
node.count ++;
update(node.left, val);
} else if (val > node.val) {
update(node.right, val);
} else {
node.count ++;
}
}
private int smaller(Node node, long val) {
if (node == null) return 0;
if (node.val <= val) {
return node.count + smaller(node.right, val);
} else {
return smaller(node.left, val);
}
}
public int countRangeSum(int[] nums, int lower, int upper) {
if (nums == null || nums.length == 0) return 0;
long[] sums = new long[nums.length];
sums[0] = nums[0];
for(int i=1; i<nums.length; i++) sums[i] = sums[i-1] + nums[i];
Set<Long> set = new HashSet<>();
for(int i=0; i<nums.length; i++) set.add(sums[i]);
long[] unique = new long[set.size()];
int u=0;
for(long s: set) unique[u++] = s;
Arrays.sort(unique);
Node root = build(unique, 0, unique.length-1);
int count = 0;
for(int i=0; i<sums.length; i++) {
if (lower <= sums[i] && sums[i] <= upper) count ++;
count += smaller(root, sums[i]-lower) - smaller(root, sums[i]-upper-1);
update(root, sums[i]);
}
return count;
}
}
class Node {
long val;
int count;
Node left, right;
Node(long val) {
this.val = val;
}
}
方法四:使用分段树,通过预先排序实现数的平衡。
public class Solution {
private Node root;
private Node build(long[] ranges, int min, int max) {
if (min>max) return null;
Node Node = new Node(ranges[min], ranges[max]);
if (min==max) return Node;
int m = (min+max)/2;
Node.left = build(ranges, min, m);
Node.right = build(ranges, m+1, max);
return Node;
}
private void update(Node node, long val) {
if (node == null) return;
if (val < node.min || node.max < val) return;
node.count ++;
update(node.left, val);
update(node.right, val);
}
private int query(Node node, long from, long to) {
if (node==null) return 0;
if (node.max < from || to < node.min) return 0;
if (from <= node.min && node.max <= to) return node.count;
return query(node.left, from, to) + query(node.right, from, to);
}
public int countRangeSum(int[] nums, int lower, int upper) {
if (nums == null || nums.length == 0) return 0;
long[] sums = new long[nums.length];
sums[0] = nums[0];
Set<Long> set = new HashSet<>();
set.add(sums[0]);
for(int i=1; i<nums.length; i++) {
sums[i] = sums[i-1] + nums[i];
set.add(sums[i]);
}
long[] ranges = new long[set.size()];
int r=0;
for(long s: set) ranges[r++] = s;
Arrays.sort(ranges);
root = build(ranges, 0, ranges.length-1);
int count = 0;
for(int i=0; i<sums.length; i++) {
if (lower <= sums[i] && sums[i] <= upper) count ++;
count += query(root, sums[i]-upper, sums[i]-lower);
update(root, sums[i]);
}
return count;
}
}
class Node {
long min, max;
int count;
Node left, right;
Node(long min, long max) {
this.min = min;
this.max = max;
}
}
方法五:使用合并排序发实现分治策略。
public class Solution {
private int sort(long[] sums, int from, int to, int lower, int upper) {
if (from > to) return 0;
if (from == to) return (lower <= sums[from] && sums[from] <= upper) ? 1 : 0;
int count = 0;
int m = (from + to) / 2;
count = sort(sums, from, m, lower, upper) + sort(sums, m+1, to, lower, upper);
for(int i=from, j=m+1, k=m+1; i<=m; i++) {
while (j<=to && sums[j] - sums[i] < lower) j++;
while (k<=to && sums[k] - sums[i] <= upper) k++;
count += k - j;
}
long[] merged = new long[to-from+1];
int l=from, r=m+1;
for(int i=0, j=from, k=m+1; i<merged.length; i++) {
if (j>m) merged[i] = sums[k++];
else if (k>to) merged[i] = sums[j++];
else if (sums[j] <= sums[k]) merged[i] = sums[j++];
else merged[i] = sums[k++];
}
System.arraycopy(merged, 0, sums, from, merged.length);
return count;
}
public int countRangeSum(int[] nums, int lower, int upper) {
if (nums == null || nums.length == 0) return 0;
long[] sums = new long[nums.length];
sums[0] = nums[0];
for(int i=1; i<nums.length; i++) sums[i] = sums[i-1] + nums[i];
return sort(sums, 0, sums.length-1, lower, upper);
}
}
方法六:树状数组。
未实现,留待下次。
二叉树不一定要用指针实现,可以用数组实现。