Problem statement
Brute force
We can solve this problem via brute force as follows. First, let $N$ be the length of the
heights array and define the set $ S = \lbrace (m, n) \mid (m, n) \in \lbrace 0, \dots, N-1 \rbrace^2, m < n\ \rbrace$. Then, for each $(i, j) \in S$, let
be the area of water contained between the $i$th and $j$th bars. Our goal is to compute
The simplest way to do this is to iterate over every pair $(i, j)$ in $S$, compute $A(i, j)$,
and then pick the largest area. However, this requires $O(N^2)$ iterations over the
heights array. Can we do better?
Optimized solution
Note that
where $Q = \lbrace A(i, j) \mid (i, j) \in S \rbrace$. The key insight that leads us to the optimized solution is that we can remove elements from set $Q$ without affecting $A^*$. The following results will allow us to do this.
First, let $h_i = \texttt{heights[i]}$ for each $i = 0,\dots,N-1$. Then, if $h_i \leq h_j$, $A(i,k) \leq A(i,j)$ for every $k$ such that $i < k < j$. In other words, if $h_i \leq h_j$, $A(i, k)$ is dominated by $A(i, j)$ for every $k$ that is between $i$ and $j$.
We prove this statement as follows. Suppose that $h_i \leq h_j$ such that $A(i, j) = (j-i)\min(h_i,h_j) = (j-i)h_i$. Then, choose any $k$ such that $i < k < j$. Hence,
Similarly, if $h_i > h_j$, $A(k, j) \leq A(i,j)$ for every $k$ such that $i < k < j$ and a similar proof follows.
These dominance results allow us to remove elements from set $Q$ as follows:
- Pick any $(i, j) \in S$.
- If $h_i \leq h_j$, remove all elements $A(i,k)$ from set $Q$ for $i < k < j$.
- Otherwise, if $h_i > h_j$, remove all elements $A(k, j)$ from set $Q$ for $i < k < j$.
- Repeat steps 1-3 as needed using different choices of $(i, j) \in S$.
Additionally, because $A(i, j)$ dominates both $A(i,k)$ and $A(k, j)$ for $i < k < j$, we should aim to choose $(i, j) \in S$ such that $|j - i|$ is as large as possible. This choice allows us to remove as many elements from set $Q$ as possible. Notice that $A^*$ is not removed from set $Q$ because it dominates all other $A(i, j)$.
Putting these results all together, we can solve this problem as follows:
- Initialize two pointers
landrsuch thatl = 0andr = len(heights) - 1. Choosinglandrthis way allows us to compute an $A(l, r)$ that dominates many other $A(i, j)$, as shown above. - Initialize a running maximum
max_area. As we eliminate $A(i, j)$ from set $Q$, we keep track of which $A(i, j)$ was the largest we’ve seen so far. - While
l < r:
a. Computearea = min(heights[l], heights[r]) * (r - l).
b. Update the largest area seen so far by assigningmax_area = max(area, max_area).
c. Ifheights[l] <= heights[r], we can remove all elements $A(l,k)$ from set $Q$ for $l < k < r$. So, to move to another $A(i, j)$, we incrementland avoid decrementingr, since this will only lead to a smaller area.
d. Otherwise, ifheights[l] > heights[r], we can remove all elements $A(k, r)$ from set $Q$ for $l < k < r$. So, to move to another $A(i, j)$, we decrementrand avoid incrementingl, since this will only lead to a smaller area. - Return
max_area.
Because we iterate over the heights array only once, this solution requires $O(N)$ time.
Because we do not create any new data structures that scale in size as a function of $N$,
we use $O(1)$ space.
This optimized solution can be implemented in Python as follows:
1
2
3
4
5
6
7
8
9
10
11
12
class Solution:
def maxArea(self, heights: List[int]) -> int:
l, r = 0, len(heights) - 1
max_area = -1
while l < r:
area = min(heights[l], heights[r]) * (r - l)
max_area = max(area, max_area)
if heights[l] < heights[r]:
l += 1
else:
r -= 1
return max_area