Skip to content
dsa450for five year olds

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

heaptray of k sums (min heap)
354012354
array
0123sum 32-131

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.

  1. For each start i, keep a running sum while you move the end j to the right. Each step gives one subarray sum.
  2. Put all sums in a list and sort it from big to small.
  3. Return the item at index k - 1.
java
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.

  1. Make the sums the same way, with a running sum for each start.
  2. For each sum s:
    • If the heap holds fewer than k sums, add s.
    • Else if s is bigger than the top, remove the top and add s.
    • Else skip s.
  3. The top of the heap is the k-th largest sum.

3rd largest subarray sum of [2, -1, 3, 1]

01 / 11

heaptray of k sums (min heap)
202
array
01232-131ji

Stretch [0..0] has sum 2. The tray has room, so 2 goes in.

sum
2
  • looking now
  • checking
  • answer
  • just added
java
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 long for sums. Many big numbers added up can pass the int limit.
  • A running sum makes each new sum O(1). Re-adding the whole stretch every time turns the work into O(n^3).
  • k can be as big as n (n + 1) / 2. Then the heap holds every sum, and you are back to O(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 guess x, count the stretches with sum at least x using prefix sums and a merge sort count, O(n log n) per guess. The whole thing is O(n log n log R), where R is the range of sums.
  • If all numbers are positive? Sums grow as the stretch grows, so for a guess x two pointers count stretches with sum at least x in O(n).
  • The k-th smallest sum? Use a max heap of size k and 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.