This Stack Overflow question has yet again nerd-sniped me

[finding the] maximum element in the array that would result from performing all M operations

Here’s the question by John that was looking for a Java solution:

With an array of N elements which are initialized to 0. we are given a sequence of M operations of the sort

`(p; q; r)`

. The operation`(p; q; r)`

signifies that the integer r should be added to all array elements`A[p];A[p + 1]; : : : ;A[q]`

. You are to output the maximum element in the array that would result from performing all M operations. There is a naive solution that simply performs all operations and then returns the maximum value, that takes`O(MN)`

time. We are looking for a more efficient algorithm.

Interesting. Indeed, a naive solution would just perform all the operations as requested. Another naive but less naive solution would transform the operations into signals of the form `(x; y)`

for all `(p; r)`

and for all `(q + 1; -r)`

. In other words, we could implement the solution I had presented trivially as such:

// This is just a utility class to model the ops class Operation { final int p; final int q; final int r; Operation(int p, int q, int r) { this.p = p; this.q = q; this.r = r; } } // These are some example ops Operation[] ops = { new Operation(4, 12, 2), new Operation(2, 8, 3), new Operation(6, 7, 1), new Operation(3, 7, 2) }; // Here, we're calculating the min and max // values for the combined values of p and q IntSummaryStatistics stats = Stream .of(ops) .flatMapToInt(op -> IntStream.of(op.p, op.q)) .summaryStatistics(); // Create an array for all the required elements using // the min value as "offset" int[] array = new int[stats.getMax() - stats.getMin()]; // Put +r and -r "signals" into the array for each op for (Operation op : ops) { int lo = op.p - stats.getMin(); int hi = op.q + 1 - stats.getMin(); if (lo >= 0) array[lo] = array[lo] + op.r; if (hi < array.length) array[hi] = array[hi] - op.r; } // Now, calculate the prefix sum sequentially in a // trivial loop int maxIndex = Integer.MIN_VALUE; int maxR = Integer.MIN_VALUE; int r = 0; for (int i = 0; i < array.length; i++) { r = r + array[i]; System.out.println((i + stats.getMin()) + ":" + r); if (r > maxR) { maxIndex = i + stats.getMin(); maxR = r; } } System.out.println("---"); System.out.println(maxIndex + ":" + maxR);

The above program would print out:

2:3 3:5 4:7 5:7 6:8 7:8 8:5 9:2 10:2 11:2 --- 6:8

So, the maximum value is generated at position 6, and the value is 8.

## Faster calculation in Java 8

This can be calculated faster using Java 8’s new Arrays.parallelPrefix() operation. Instead of the loop in the end, just write:

Arrays.parallelPrefix(array, Integer::sum); System.out.println( Arrays.stream(array).parallel().max());

Which is awesome, as it can run faster than the sequential `O(M+N)`

solution. Read up about prefix sums here.

## Now show me the promised SQL code

In SQL, the naive sequential and linear complexity solution can easily be re-implemented, and I’m showing a solution for PostgreSQL.

How can we do it? We’re using a couple of features here. First off, we’re using common table expressions (also known as the `WITH`

clause). We’re using these to declare table variables. The first variable is the `op`

table, which contains our operation instructions, like in Java:

WITH op (p, q, r) AS ( VALUES (4, 12, 2), (2, 8, 3), (6, 7, 1), (3, 7, 2) ), ...

This is trivial. We’re essentially just generating a couple of example values.

The second table variable is the signal table, where we use the previously described optimisation of putting a `+r`

signal at all `p`

positions, and a `-r`

signal at all `q + 1`

positions:

WITH ..., signal(x, r) AS ( SELECT p, r FROM op UNION ALL SELECT q + 1, -r FROM op ) ...

When you run

SELECT * FROM signal ORDER BY x

you would simply get:

x r ------ 2 3 3 2 4 2 6 1 8 -2 8 -1 9 -3 13 -2

All we need to do now is calculate a running total (which is essentially the same as a prefix sum) as follows:

SELECT x, SUM(r) OVER (ORDER BY x) FROM signal ORDER BY x

x r ------ 2 3 3 5 4 7 6 8 8 5 8 5 9 2 13 0

Now just find the max value for `r`

, and we’re all set. We’ll take the shortcut by using `ORDER BY`

and `LIMIT`

:

SELECT x, SUM(r) OVER (ORDER BY x) AS s FROM signal ORDER BY s DESC LIMIT 1

And we’re back with:

x r ------ 6 8

Perfect! Here’s the full query:

WITH op (p, q, r) AS ( VALUES (4, 12, 2), (2, 8, 3), (6, 7, 1), (3, 7, 2) ), signal(x, r) AS ( SELECT p, r FROM op UNION ALL SELECT q + 1, -r FROM op ) SELECT x, SUM(r) OVER (ORDER BY x) AS s FROM signal ORDER BY s DESC LIMIT 1

Can you beat the conciseness of this SQL solution? I bet you can’t. Challengers shall write alternatives in the comment section.

Thrilled about the SQL here? Read about how to calculate a subset sum in Oracle SQL.