Heap · 8 of 18
Kth largest sum continuous subarrays
Look at the total of every unbroken stretch of numbers and find the k-th biggest total, keeping only k totals at a time.
- Simple way
- time O(n^2 log n)
room O(n^2) - Clever way
- time O(n^2 log k)
room O(k)
The problem, simply
You get an array of numbers and a number k. A subarray is an unbroken stretch of the array, like arr[1..3]. Every subarray has a sum. An array of n numbers has n (n + 1) / 2 subarrays. Find the k-th largest of all those sums.
For [3, 2, 1] the sums are 3, 5, 6, 2, 3 and 1. From big to small: 6, 5, 3, 3, 2, 1. With k = 2 the answer is 5.
A story to remember it
Picture it
The stretch from index 1 to 3 of [2, -1, 3, 1] has sum -1 + 3 + 1 = 3. A min heap of size 3 keeps the three biggest sums seen so far.
- checking
Examples
A small case
- In
arr = [3, 2, 1], k = 2- Out
5
The sums are 3, 5, 6, 2, 3, 1. The biggest is 6 and the second biggest is 5.
Negative numbers
- In
arr = [10, -10, 20, -40], k = 6- Out
-10
The ten sums from big to small are 20, 20, 10, 10, 0, -10, -20, -20, -30, -40. The 6th is -10.
Repeated sums count twice
- In
arr = [2, -1, 3, 1], k = 3- Out
4
The sums from big to small are 5, 4, 4, 3, 3, 2, 2, 1, 1, -1. The sum 4 shows up twice (2 - 1 + 3 and 3 + 1), and both count.
One number
- In
arr = [7], k = 1- Out
7
There is only one subarray.
Way 1: the simple way
Write every sum on the board, sort the board, and read the k-th from the top.
- For each start
i, keep a running sum while you move the endjto the right. Each step gives one subarray sum. - Put all sums in a list and sort it from big to small.
- Return the item at index
k - 1.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Solution {
public long kthLargestSum(int[] arr, int k) {
List<Long> sums = new ArrayList<>();
for (int i = 0; i < arr.length; i++) {
long sum = 0;
for (int j = i; j < arr.length; j++) {
sum += arr[j]; // the sum of arr[i..j]
sums.add(sum);
}
}
Collections.sort(sums, Collections.reverseOrder());
return sums.get(k - 1);
}
}How long it takes: there are about n^2 / 2 sums, and sorting them costs O(n^2 log n). Extra room: the list of all sums, O(n^2). For n = 10,000 that is 50 million numbers.
Way 2: the clever way
Do not keep every sum. Keep only the k biggest so far in a min heap, so the smallest of them is on top.
- Make the sums the same way, with a running sum for each start.
- For each sum
s:- If the heap holds fewer than
ksums, adds. - Else if
sis bigger than the top, remove the top and adds. - Else skip
s.
- If the heap holds fewer than
- The top of the heap is the k-th largest sum.
3rd largest subarray sum of [2, -1, 3, 1]
01 / 11
Stretch [0..0] has sum 2. The tray has room, so 2 goes in.
- sum
- 2
- looking now
- checking
- answer
- just added
import java.util.PriorityQueue;
class Solution {
public long kthLargestSum(int[] arr, int k) {
PriorityQueue<Long> tray = new PriorityQueue<>(); // min heap of size k
for (int i = 0; i < arr.length; i++) {
long sum = 0;
for (int j = i; j < arr.length; j++) {
sum += arr[j]; // sum of arr[i..j]
if (tray.size() < k) {
tray.offer(sum);
} else if (sum > tray.peek()) {
tray.poll(); // smallest of the k leaves
tray.offer(sum);
}
}
}
return tray.peek();
}
}How long it takes: there are about n^2 / 2 sums and each costs at most O(log k) on the heap, so O(n^2 log k). Extra room: the heap, O(k).
Watch out for
- Use
longfor sums. Many big numbers added up can pass theintlimit. - A running sum makes each new sum
O(1). Re-adding the whole stretch every time turns the work intoO(n^3). kcan be as big asn (n + 1) / 2. Then the heap holds every sum, and you are back toO(n^2)room.- With negative numbers, sums can go down as the stretch grows. Do not stop early when the sum drops.
What interviewers ask next
- Can you beat
O(n^2)? Yes, binary search on the answer. For a guessx, count the stretches with sum at leastxusing prefix sums and a merge sort count,O(n log n)per guess. The whole thing isO(n log n log R), whereRis the range of sums. - If all numbers are positive? Sums grow as the stretch grows, so for a guess
xtwo pointers count stretches with sum at leastxinO(n). - The k-th smallest sum? Use a max heap of size
kand skip sums that are bigger than the top. - The k largest sums from two sorted arrays (pair sums)? Start a max heap with the best pair and push its two neighbors each time you pop, with a set to avoid repeats.
Remember this
Grown-up words: a run of wagons is a contiguous subarray, the running count is a prefix sum, the tray is a min heap (priority queue) of size k, and the front card is the heap top.