Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
Given an integer array nums and two integers lower and upper, 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 inclusive, where i <= j.
Example 1:
Input: nums = [-2,5,-1], lower = -2, upper = 2 Output: 3 Explanation: The three ranges are: [0,0], [2,2], and [0,2] and their respective sums are: -2, -1, 2.
Example 2:
Input: nums = [0], lower = 0, upper = 0 Output: 1
Constraints:
1 <= nums.length <= 105-231 <= nums[i] <= 231 - 1-105 <= lower <= upper <= 105Problem summary: Given an integer array nums and two integers lower and upper, 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 inclusive, where i <= j.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Binary Search · Segment Tree
[-2,5,-1] -2 2
[0] 0 0
count-of-smaller-numbers-after-self)reverse-pairs)count-the-number-of-fair-pairs)find-the-number-of-copy-arrays)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #327: Count of Range Sum
class BinaryIndexedTree {
private int n;
private int[] c;
public BinaryIndexedTree(int n) {
this.n = n;
this.c = new int[n + 1];
}
public void update(int x, int v) {
while (x <= n) {
c[x] += v;
x += x & -x;
}
}
public int query(int x) {
int s = 0;
while (x != 0) {
s += c[x];
x -= x & -x;
}
return s;
}
}
class Solution {
public int countRangeSum(int[] nums, int lower, int upper) {
int n = nums.length;
long[] s = new long[n + 1];
for (int i = 0; i < n; ++i) {
s[i + 1] = s[i] + nums[i];
}
long[] arr = new long[n * 3 + 3];
for (int i = 0, j = 0; i <= n; ++i, j += 3) {
arr[j] = s[i];
arr[j + 1] = s[i] - lower;
arr[j + 2] = s[i] - upper;
}
Arrays.sort(arr);
int m = 0;
for (int i = 0; i < arr.length; ++i) {
if (i == 0 || arr[i] != arr[i - 1]) {
arr[m++] = arr[i];
}
}
BinaryIndexedTree tree = new BinaryIndexedTree(m);
int ans = 0;
for (long x : s) {
int l = search(arr, m, x - upper);
int r = search(arr, m, x - lower);
ans += tree.query(r) - tree.query(l - 1);
tree.update(search(arr, m, x), 1);
}
return ans;
}
private int search(long[] nums, int r, long x) {
int l = 0;
while (l < r) {
int mid = (l + r) >> 1;
if (nums[mid] >= x) {
r = mid;
} else {
l = mid + 1;
}
}
return l + 1;
}
}
// Accepted solution for LeetCode #327: Count of Range Sum
type BinaryIndexedTree struct {
n int
c []int
}
func newBinaryIndexedTree(n int) *BinaryIndexedTree {
c := make([]int, n+1)
return &BinaryIndexedTree{n, c}
}
func (this *BinaryIndexedTree) update(x, delta int) {
for x <= this.n {
this.c[x] += delta
x += x & -x
}
}
func (this *BinaryIndexedTree) query(x int) int {
s := 0
for x > 0 {
s += this.c[x]
x -= x & -x
}
return s
}
func countRangeSum(nums []int, lower int, upper int) (ans int) {
n := len(nums)
s := make([]int, n+1)
for i, x := range nums {
s[i+1] = s[i] + x
}
arr := make([]int, (n+1)*3)
for i, j := 0, 0; i <= n; i, j = i+1, j+3 {
arr[j] = s[i]
arr[j+1] = s[i] - lower
arr[j+2] = s[i] - upper
}
sort.Ints(arr)
m := 0
for i := range arr {
if i == 0 || arr[i] != arr[i-1] {
arr[m] = arr[i]
m++
}
}
arr = arr[:m]
tree := newBinaryIndexedTree(m)
for _, x := range s {
l := sort.SearchInts(arr, x-upper) + 1
r := sort.SearchInts(arr, x-lower) + 1
ans += tree.query(r) - tree.query(l-1)
tree.update(sort.SearchInts(arr, x)+1, 1)
}
return
}
# Accepted solution for LeetCode #327: Count of Range Sum
class BinaryIndexedTree:
def __init__(self, n):
self.n = n
self.c = [0] * (n + 1)
def update(self, x, v):
while x <= self.n:
self.c[x] += v
x += x & -x
def query(self, x):
s = 0
while x > 0:
s += self.c[x]
x -= x & -x
return s
class Solution:
def countRangeSum(self, nums: List[int], lower: int, upper: int) -> int:
s = list(accumulate(nums, initial=0))
arr = sorted(set(v for x in s for v in (x, x - lower, x - upper)))
tree = BinaryIndexedTree(len(arr))
ans = 0
for x in s:
l = bisect_left(arr, x - upper) + 1
r = bisect_left(arr, x - lower) + 1
ans += tree.query(r) - tree.query(l - 1)
tree.update(bisect_left(arr, x) + 1, 1)
return ans
// Accepted solution for LeetCode #327: Count of Range Sum
/**
* [327] Count of Range Sum
*
* Given an integer array nums and two integers lower and upper, 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 inclusive, where i <= j.
*
* Example 1:
*
* Input: nums = [-2,5,-1], lower = -2, upper = 2
* Output: 3
* Explanation: The three ranges are: [0,0], [2,2], and [0,2] and their respective sums are: -2, -1, 2.
*
* Example 2:
*
* Input: nums = [0], lower = 0, upper = 0
* Output: 1
*
*
* Constraints:
*
* 1 <= nums.length <= 10^5
* -2^31 <= nums[i] <= 2^31 - 1
* -10^5 <= lower <= upper <= 10^5
* The answer is guaranteed to fit in a 32-bit integer.
*
*/
pub struct Solution {}
// problem: https://leetcode.com/problems/count-of-range-sum/
// discuss: https://leetcode.com/problems/count-of-range-sum/discuss/?currentPage=1&orderBy=most_votes&query=
// submission codes start here
impl Solution {
// Credit: https://leetcode.com/problems/count-of-range-sum/discuss/1378661/Rust-Divide-and-Conquer-using-prefix-sum
pub fn count_range_sum(nums: Vec<i32>, lower: i32, upper: i32) -> i32 {
Self::count_sub_range_sum(
std::rc::Rc::new(std::cell::RefCell::new(
Some(0 as i64)
.iter()
.chain(nums.iter().map(|x| *x as i64).collect::<Vec<i64>>().iter())
.scan(0, |state, x| {
*state += x;
Some(*state as i64)
})
.collect::<Vec<i64>>(),
)),
0,
nums.len(),
lower as i64,
upper as i64,
)
}
fn count_sub_range_sum(
prefix: std::rc::Rc<std::cell::RefCell<Vec<i64>>>,
start: usize,
end: usize,
lower: i64,
upper: i64,
) -> i32 {
if start >= end {
return 0;
}
// Pre-compute the two halves, thus assuming both sorted
let mid: usize = (start + end) / 2;
let mut count: i32 =
Self::count_sub_range_sum(std::rc::Rc::clone(&prefix), start, mid, lower, upper)
+ Self::count_sub_range_sum(
std::rc::Rc::clone(&prefix),
mid + 1,
end,
lower,
upper,
);
// loop over every pos in left and use two pointers for lower & upper
let (mut i, mut j) = (mid + 1, mid + 1);
for pos in start..=mid {
// compute the range of acceptable values
while i <= end && (*prefix.borrow())[i] - (*prefix.borrow())[pos] < lower {
i += 1;
}
while j <= end && (*prefix.borrow())[j] - (*prefix.borrow())[pos] <= upper {
j += 1;
}
count += (j - i) as i32;
}
// partially sort the vector
let mut replacement = (*prefix.borrow())[start..=end].to_vec();
replacement.sort();
(*prefix.borrow_mut())[start..=end].copy_from_slice(&replacement[..]);
count
}
}
// submission codes end
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_0327_example_1() {
let nums = vec![-2, 5, -1];
let lower = -2;
let upper = 2;
let result = 3;
assert_eq!(Solution::count_range_sum(nums, lower, upper), result);
}
#[test]
fn test_0327_example_2() {
let nums = vec![0];
let lower = 0;
let upper = 0;
let result = 1;
assert_eq!(Solution::count_range_sum(nums, lower, upper), result);
}
}
// Accepted solution for LeetCode #327: Count of Range Sum
class BinaryIndexedTree {
private n: number;
private c: number[];
constructor(n: number) {
this.n = n;
this.c = Array(n + 1).fill(0);
}
update(x: number, v: number) {
while (x <= this.n) {
this.c[x] += v;
x += x & -x;
}
}
query(x: number): number {
let s = 0;
while (x > 0) {
s += this.c[x];
x -= x & -x;
}
return s;
}
}
function countRangeSum(nums: number[], lower: number, upper: number): number {
const n = nums.length;
const s = Array(n + 1).fill(0);
for (let i = 0; i < n; ++i) {
s[i + 1] = s[i] + nums[i];
}
let arr: number[] = Array((n + 1) * 3);
for (let i = 0, j = 0; i <= n; ++i, j += 3) {
arr[j] = s[i];
arr[j + 1] = s[i] - lower;
arr[j + 2] = s[i] - upper;
}
arr.sort((a, b) => a - b);
let m = 0;
for (let i = 0; i < arr.length; ++i) {
if (i === 0 || arr[i] !== arr[i - 1]) {
arr[m++] = arr[i];
}
}
arr = arr.slice(0, m);
const tree = new BinaryIndexedTree(m);
let ans = 0;
for (const x of s) {
const l = search(arr, m, x - upper);
const r = search(arr, m, x - lower);
ans += tree.query(r) - tree.query(l - 1);
tree.update(search(arr, m, x), 1);
}
return ans;
}
function search(nums: number[], r: number, x: number): number {
let l = 0;
while (l < r) {
const mid = (l + r) >> 1;
if (nums[mid] >= x) {
r = mid;
} else {
l = mid + 1;
}
}
return l + 1;
}
Use this to step through a reusable interview workflow for this problem.
Check every element from left to right until we find the target or exhaust the array. Each comparison is O(1), and we may visit all n elements, giving O(n). No extra space needed.
Each comparison eliminates half the remaining search space. After k comparisons, the space is n/2ᵏ. We stop when the space is 1, so k = log₂ n. No extra memory needed — just two pointers (lo, hi).
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Setting `lo = mid` or `hi = mid` can stall and create an infinite loop.
Usually fails on: Two-element ranges never converge.
Fix: Use `lo = mid + 1` or `hi = mid - 1` where appropriate.