Facebook Pixel

1918. Kth Smallest Subarray Sum

Problem Description

You are given an integer array nums of length n and an integer k. Your task is to find the k-th smallest subarray sum.

A subarray is a contiguous sequence of elements from the array that contains at least one element. The subarray sum is the sum of all elements in that subarray.

For example, if nums = [1, 2, 3], the possible subarrays are:

  • [1] with sum 1
  • [2] with sum 2
  • [3] with sum 3
  • [1, 2] with sum 3
  • [2, 3] with sum 5
  • [1, 2, 3] with sum 6

If we sort all possible subarray sums in ascending order, we get: 1, 2, 3, 3, 5, 6. The k-th smallest value would be the element at position k in this sorted list (1-indexed).

The challenge is to find this k-th smallest sum efficiently without explicitly calculating and sorting all possible subarray sums, as there are n*(n+1)/2 possible subarrays which could be very large.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is recognizing a monotonic property: if we pick a target sum value s, we can count how many subarrays have a sum less than or equal to s. As s increases, this count also increases. This creates a feasibility sequence:

Sum s:      1    2    3    4    5    6    ...
Count ≥ k:  F    F    F    T    T    T    ...
                   first_true_index

We want to find the first sum value where at least k subarrays have sum ≤ that value. This is exactly the "find first true" pattern for binary search.

The search space ranges from min(nums) (smallest possible subarray sum) to sum(nums) (largest possible subarray sum).

For each candidate sum mid, we define feasible(mid) as: are there at least k subarrays with sum ≤ mid? We count using a sliding window - since all elements are positive, when the window sum exceeds mid, we shrink from the left. At each position i, subarrays ending at i with starting positions from j to i are valid, giving i - j + 1 subarrays.

Learn more about Binary Search and Sliding Window patterns.

Solution Implementation

1class Solution:
2    def kthSmallestSubarraySum(self, nums: List[int], k: int) -> int:
3        def feasible(target: int) -> bool:
4            """Check if at least k subarrays have sum <= target."""
5            count = 0
6            window_sum = 0
7            left = 0
8
9            for right, num in enumerate(nums):
10                window_sum += num
11                while window_sum > target:
12                    window_sum -= nums[left]
13                    left += 1
14                count += right - left + 1
15
16            return count >= k
17
18        # Binary search with standard template
19        left, right = min(nums), sum(nums)
20        first_true_index = -1
21
22        while left <= right:
23            mid = (left + right) // 2
24
25            if feasible(mid):
26                first_true_index = mid
27                right = mid - 1  # Search for smaller valid sum
28            else:
29                left = mid + 1
30
31        return first_true_index
32
1class Solution {
2    public int kthSmallestSubarraySum(int[] nums, int k) {
3        int minVal = Integer.MAX_VALUE;
4        int total = 0;
5        for (int num : nums) {
6            minVal = Math.min(minVal, num);
7            total += num;
8        }
9
10        // Binary search with standard template
11        int left = minVal;
12        int right = total;
13        int firstTrueIndex = -1;
14
15        while (left <= right) {
16            int mid = left + (right - left) / 2;
17
18            if (countSubarrays(nums, mid) >= k) {
19                firstTrueIndex = mid;
20                right = mid - 1;  // Search for smaller valid sum
21            } else {
22                left = mid + 1;
23            }
24        }
25
26        return firstTrueIndex;
27    }
28
29    private int countSubarrays(int[] nums, int target) {
30        int count = 0;
31        int windowSum = 0;
32        int left = 0;
33
34        for (int right = 0; right < nums.length; right++) {
35            windowSum += nums[right];
36            while (windowSum > target) {
37                windowSum -= nums[left];
38                left++;
39            }
40            count += right - left + 1;
41        }
42        return count;
43    }
44}
45
1class Solution {
2public:
3    int kthSmallestSubarraySum(vector<int>& nums, int k) {
4        int minVal = INT_MAX;
5        int total = 0;
6        for (int num : nums) {
7            minVal = min(minVal, num);
8            total += num;
9        }
10
11        auto countSubarrays = [&](int target) {
12            int count = 0;
13            int windowSum = 0;
14            int left = 0;
15
16            for (int right = 0; right < nums.size(); right++) {
17                windowSum += nums[right];
18                while (windowSum > target) {
19                    windowSum -= nums[left];
20                    left++;
21                }
22                count += right - left + 1;
23            }
24            return count;
25        };
26
27        // Binary search with standard template
28        int left = minVal;
29        int right = total;
30        int firstTrueIndex = -1;
31
32        while (left <= right) {
33            int mid = left + (right - left) / 2;
34
35            if (countSubarrays(mid) >= k) {
36                firstTrueIndex = mid;
37                right = mid - 1;  // Search for smaller valid sum
38            } else {
39                left = mid + 1;
40            }
41        }
42
43        return firstTrueIndex;
44    }
45};
46
1function kthSmallestSubarraySum(nums: number[], k: number): number {
2    const countSubarrays = (target: number): number => {
3        let count = 0;
4        let windowSum = 0;
5        let left = 0;
6
7        for (let right = 0; right < nums.length; right++) {
8            windowSum += nums[right];
9            while (windowSum > target) {
10                windowSum -= nums[left];
11                left++;
12            }
13            count += right - left + 1;
14        }
15        return count;
16    };
17
18    // Binary search with standard template
19    let left = Math.min(...nums);
20    let right = nums.reduce((sum, num) => sum + num, 0);
21    let firstTrueIndex = -1;
22
23    while (left <= right) {
24        const mid = Math.floor((left + right) / 2);
25
26        if (countSubarrays(mid) >= k) {
27            firstTrueIndex = mid;
28            right = mid - 1;  // Search for smaller valid sum
29        } else {
30            left = mid + 1;
31        }
32    }
33
34    return firstTrueIndex;
35}
36

Solution Approach

The solution uses binary search with the standard template, combined with a sliding window counting function.

Template Structure:

left, right = min(nums), sum(nums)
first_true_index = -1

while left <= right:
    mid = (left + right) // 2
    if feasible(mid):  # count of subarrays with summid >= k
        first_true_index = mid
        right = mid - 1  # Search for smaller valid sum
    else:
        left = mid + 1

return first_true_index

The Feasibility Function:

Count subarrays with sum ≤ target using sliding window:

  1. Initialize: window_sum = 0, left_ptr = 0, count = 0
  2. For each right_ptr:
    • Add nums[right_ptr] to window_sum
    • While window_sum > target: shrink window from left
    • Add right_ptr - left_ptr + 1 to count (all valid subarrays ending at right_ptr)
  3. Return count >= k

Why This Works:

We're finding the first (smallest) sum value where at least k subarrays exist. When feasible, we record first_true_index = mid and search left for potentially smaller values. When not feasible, we search right.

Time Complexity: O(n × log S) where S = sum(nums) - min(nums).

Space Complexity: O(1).

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through finding the 4th smallest subarray sum for nums = [2, 1, 3] and k = 4.

Step 1: Initialize

  • left = min(nums) = 1, right = sum(nums) = 6
  • first_true_index = -1

Step 2: Binary search iterations

Iteration 1: left = 1, right = 6

  • mid = (1 + 6) // 2 = 3
  • Count subarrays with sum ≤ 3:
    • Position 0: sum = 2, count += 1 → total = 1
    • Position 1: sum = 2+1 = 3, count += 2 → total = 3
    • Position 2: sum = 3+3 = 6 > 3, shrink → sum = 3, count += 1 → total = 4
  • count = 4 >= k = 4 ✓ feasible
  • Update: first_true_index = 3, right = 3 - 1 = 2

Iteration 2: left = 1, right = 2

  • mid = (1 + 2) // 2 = 1
  • Count subarrays with sum ≤ 1:
    • Position 0: sum = 2 > 1, shrink → empty, count += 0
    • Position 1: sum = 1, count += 1 → total = 1
    • Position 2: sum = 1+3 = 4 > 1, shrink → sum = 3 > 1, shrink → empty
  • count = 1 < k = 4 ✗ not feasible
  • Update: left = 1 + 1 = 2

Iteration 3: left = 2, right = 2

  • mid = (2 + 2) // 2 = 2
  • Count subarrays with sum ≤ 2:
    • Position 0: sum = 2, count += 1
    • Position 1: sum = 2+1 = 3 > 2, shrink → sum = 1, count += 1 → total = 2
    • Position 2: sum = 1+3 = 4 > 2, shrink → sum = 3 > 2, shrink → empty
  • count = 2 < k = 4 ✗ not feasible
  • Update: left = 2 + 1 = 3

Step 3: Loop terminates

  • left = 3 > right = 2
  • Return first_true_index = 3

The 4th smallest subarray sum is 3.

Time and Space Complexity

Time Complexity: O(n * log(sum(nums) - min(nums)))

The algorithm uses binary search on the range [min(nums), sum(nums)]. The binary search range has a size of sum(nums) - min(nums), leading to O(log(sum(nums) - min(nums))) iterations.

For each binary search iteration, the helper function f(s) is called, which uses a sliding window approach to count the number of subarrays with sum ≤ s. This sliding window traversal takes O(n) time since each element is visited at most twice (once by pointer i and once by pointer j).

Therefore, the total time complexity is O(n * log(sum(nums) - min(nums))).

Space Complexity: O(1)

The algorithm uses only a constant amount of extra space:

  • Variables l, r for binary search bounds
  • Variables t, j, cnt, i, x in the helper function f
  • The range object in bisect_left is a generator that doesn't create the full list

No additional data structures that scale with input size are used, resulting in O(1) space complexity.

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Using the Wrong Binary Search Template

The Pitfall: Using while left < right with right = mid instead of the standard while left <= right template.

Solution: Use the standard template:

left, right = min(nums), sum(nums)
first_true_index = -1

while left <= right:
    mid = (left + right) // 2
    if feasible(mid):
        first_true_index = mid
        right = mid - 1
    else:
        left = mid + 1

return first_true_index

2. Incorrect Handling of Negative Numbers

The Pitfall: The sliding window technique only works when all numbers are positive.

Why it fails: With negative numbers, adding elements can decrease the sum, breaking the sliding window invariant.

Solution: This problem assumes positive numbers. For negative numbers, use a different approach (e.g., generate all sums and sort).

3. Integer Overflow

The Pitfall: The sum might exceed integer limits in languages with fixed integer sizes.

Solution: Use long in Java/C++ or be mindful of the constraints.

Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

What is the best way of checking if an element exists in an unsorted array once in terms of time complexity? Select the best that applies.


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More