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.
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
321class 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}
451class 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};
461function 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}
36Solution 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 sum ≤ mid >= 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:
- Initialize:
window_sum = 0,left_ptr = 0,count = 0 - For each
right_ptr:- Add
nums[right_ptr]towindow_sum - While
window_sum > target: shrink window from left - Add
right_ptr - left_ptr + 1to count (all valid subarrays ending atright_ptr)
- Add
- 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 EvaluatorExample 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) = 6first_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,rfor binary search bounds - Variables
t,j,cnt,i,xin the helper functionf - The
rangeobject inbisect_leftis 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.
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
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
https assets algo monster cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in the window The window
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!