Container With Most Water in Java DSA: The Two-Pointer Trick That Beats Every Loop
-
Last Updated: July 28, 2026
-
By: javahandson
-
Series
Learn Java in a easy way
Solve Container With Most Water in Java DSA the easy way. Full step-by-step dry runs of the brute force and O(n) two-pointer approaches, with clean code and diagrams.
Container With Most Water in Java is one of those problems that looks tricky but has a beautiful, simple answer. You have a row of vertical lines of different heights. Pick two of them, and they form the walls of a container. Your job is to find the pair that holds the most water.
The catch is the water level. A container can only be as tall as its shorter wall. Pour in more, and it spills over the low side. So the area you get is the shorter wall times the gap between the two walls.
We will solve this in two clear steps. First comes brute force, which checks every possible pair of walls. Then comes the two-pointer sweep, which starts wide and closes in smartly. That second one is the answer interviewers really want to see.
Every approach gets a full, step-by-step dry run on the same small array. Nothing is skipped, so you can follow each line of code and watch exactly what changes at every move.
Let us nail down the rules before we write any code.
The area of any container follows one small formula. Multiply the width by the shorter wall. In symbols that is (higher index minus lower index) times min(height at lower, height at higher).
For our array the best answer is 16. That comes from the line of height 8 at index 1 and the line of height 4 at index 5. The width is 4 and the shorter wall is 4, so 4 times 4 gives 16.
This one idea drives the whole problem. A wide container with a tall short-wall wins. A narrow one, or one with a tiny short wall, loses. Keep the formula in your head and everything else falls into place.
Start with the widest possible container. Put one pointer at the far left and one at the far right. Then walk them toward each other, one step at a time.
Why move the shorter wall? The width can only shrink as the pointers close in. So the only hope for a bigger area is a taller short wall. Moving the tall side away would waste width for nothing.
| 💡 Interview Insight A classic follow-up is why we always move the shorter line. Say this: width only shrinks going inward, so the taller wall never limits us, and only a new taller short wall can grow the area. Moving the tall side can never help. |
Try every pair of lines. Work out the water each pair holds, and remember the biggest. It is slow, but it proves you understand the goal.
best = 0
for i from 0 to n-1: // left wall
for j from i+1 to n-1: // right wall
width = j - i
wall = min(height[i], height[j])
area = width * wall
if area > best:
best = area
return bestpublic class ContainerBrute {
public static int maxArea(int[] height) {
int best = 0;
for (int i = 0; i < height.length - 1; i++) {
for (int j = i + 1; j < height.length; j++) {
int width = j - i;
int wall = Math.min(height[i], height[j]);
int area = width * wall;
if (area > best) {
best = area;
}
}
}
return best;
}
public static void main(String[] args) {
int[] height = { 1, 8, 6, 2, 5, 4 };
System.out.println(maxArea(height)); // 16
}
}Array: [1, 8, 6, 2, 5, 4]. The two loops try every pair in order. There are 15 pairs in all, and we trace every single one. Watch the best column on the right climb and then settle.
| Step | i (val) | j (val) | width | wall = min | area | best after |
|---|---|---|---|---|---|---|
| 1 | 0 (1) | 1 (8) | 1 | 1 | 1 | 1 (new best) |
| 2 | 0 (1) | 2 (6) | 2 | 1 | 2 | 2 (new best) |
| 3 | 0 (1) | 3 (2) | 3 | 1 | 3 | 3 (new best) |
| 4 | 0 (1) | 4 (5) | 4 | 1 | 4 | 4 (new best) |
| 5 | 0 (1) | 5 (4) | 5 | 1 | 5 | 5 (new best) |
| 6 | 1 (8) | 2 (6) | 1 | 6 | 6 | 6 (new best) |
| 7 | 1 (8) | 3 (2) | 2 | 2 | 4 | 6 |
| 8 | 1 (8) | 4 (5) | 3 | 5 | 15 | 15 (new best) |
| 9 | 1 (8) | 5 (4) | 4 | 4 | 16 | 16 (new best) |
| 10 | 2 (6) | 3 (2) | 1 | 2 | 2 | 16 |
| 11 | 2 (6) | 4 (5) | 2 | 5 | 10 | 16 |
| 12 | 2 (6) | 5 (4) | 3 | 4 | 12 | 16 |
| 13 | 3 (2) | 4 (5) | 1 | 2 | 2 | 16 |
| 14 | 3 (2) | 5 (4) | 2 | 2 | 4 | 16 |
| 15 | 4 (5) | 5 (4) | 1 | 4 | 4 | 16 |
Let us walk the whole trace, group by group, and see what the loops are doing.
Steps 1 to 5: i is fixed at index 0, the wall of height 1.
The left wall is stuck on the tiny height-1 line. Now j slides right across every other line.
So the area climbs slowly to 5, held back entirely by that short left wall. This shows the trap: a short wall caps every container it belongs to.
Steps 6 to 9: i is fixed at index 1, the tall wall of height 8.
Now the left wall is the tall 8, so the right wall decides the water height in every pair here.
Notice how the tall 8 lets the right wall do the work. The best answer, 16, appears right here at step 9.
Steps 10 to 15: i moves through the remaining lines.
These passes fix i on the shorter interior lines, so nothing here can beat 16.
Fifteen pairs were tested in total, yet the best never moved past 16. Every pair got checked, which is exactly why brute force is slow.
Fifteen pairs for six lines is nothing. For a few thousand lines it crawls, which is why we sharpen it next.
Here is the clever part. Start with the widest container, then close in from both ends. Each step, move the pointer on the shorter wall. This finds the answer in a single pass, and it is the version interviewers hope to see.
lower = 0
higher = n - 1
best = 0
while lower < higher:
width = higher - lower
wall = min(height[lower], height[higher])
best = max(best, width * wall)
if height[lower] < height[higher]:
lower = lower + 1 // move the shorter wall
else:
higher = higher - 1 // move the shorter wall
return bestpublic class ContainerTwoPointer {
public static int maxArea(int[] height) {
int lower = 0, higher = height.length - 1;
int best = 0;
while (lower < higher) {
int width = higher - lower;
int wall = Math.min(height[lower], height[higher]);
best = Math.max(best, width * wall);
if (height[lower] < height[higher]) {
lower++;
} else {
higher--;
}
}
return best;
}
public static void main(String[] args) {
int[] height = { 1, 8, 6, 2, 5, 4 };
System.out.println(maxArea(height)); // 16
}
}Array: [1, 8, 6, 2, 5, 4]. We trace every stop of the while loop until the pointers meet. Only five stops are needed, far fewer than the fifteen pairs brute force checked.
| Step | lower (val) | higher (val) | width | wall = min | area | best after | which pointer moves |
|---|---|---|---|---|---|---|---|
| 1 | 0 (1) | 5 (4) | 5 | 1 | 5 | 5 | 1 < 4, move lower right |
| 2 | 1 (8) | 5 (4) | 4 | 4 | 16 | 16 | 8 > 4, move higher left |
| 3 | 1 (8) | 4 (5) | 3 | 5 | 15 | 16 | 8 > 5, move higher left |
| 4 | 1 (8) | 3 (2) | 2 | 2 | 4 | 16 | 8 > 2, move higher left |
| 5 | 1 (8) | 2 (6) | 1 | 6 | 6 | 16 | 8 > 6, move higher left |
| end | lower=2, higher=1 | — | — | — | — | 16 | pointers crossed, stop |
Legend: lower is the left pointer moving right, higher is the right pointer moving left. wall is the shorter of the two heights, and area is width times wall.
Let us go stop by stop and watch the two pointers steer. Each move follows one rule: shift the shorter wall inward.
Step 1: lower=0 (height 1), higher=5 (height 4).
We open with the widest container. The gap is 5 and the shorter wall is the height-1 line on the left.
We drop the height-1 line on purpose. It can never help again, because any container using it is capped at 1.
Step 2: lower=1 (height 8), higher=5 (height 4).
Now the left wall jumps to the tall 8. The gap shrank to 4, but the short wall grew a lot.
This is the payoff. By moving the tiny left wall away, we swapped a width of 5 for a much taller short wall, and the area more than tripled.
Step 3: lower=1 (height 8), higher=4 (height 5).
The left wall stays on 8. The right wall is now the height-5 line, and the gap is 3.
We keep the tall 8 fixed and keep testing new right walls. The width is shrinking, so a right wall would need to be very tall to win now.
Step 4: lower=1 (height 8), higher=3 (height 2).
The right wall drops to the short height-2 line, and the gap is only 2.
A short right wall plus a small width gives a tiny area. The sweep shrugs and moves on.
Step 5: lower=1 (height 8), higher=2 (height 6).
The right wall is now the height-6 line, sitting right next to the left wall. The gap is just 1.
After this move higher becomes 1 and lower is also 1, so the pointers have met. The loop condition lower < higher is now false.
End: the pointers crossed, so the sweep stops.
Across all five stops, every move followed the same rule. We measured the container, then shifted the shorter wall inward. The pointers only travelled toward each other, so we never checked the same gap twice and used no extra memory. Five stops found the same answer, 16, that brute force needed fifteen pairs to reach.
| 💡 Interview Insight Interviewers love the question, could moving the shorter wall skip the real answer? The safe reply: the container using the shorter wall at its current width is already measured, and any narrower container with that same short wall can only be smaller. So we lose nothing by leaving it behind. |
This is a clean jump from O(n squared) down to O(n). Same answer, far less work, and almost no memory.
Tables are exact, but a sketch often lands faster. Here is the same two-pointer sweep drawn by hand.

Both give the same answer. They just pay very different prices.
| Approach | How it searches | Time | Space | Note |
|---|---|---|---|---|
| Brute force | Two loops, every pair | O(n²) | O(1) | Simple, but slow on big arrays |
| Two pointers | One pass, close inward | O(n) | O(1) | Fast, lean, the expected answer |
The two share the same tiny space cost. What separates them is time. Brute force checks every pair, while the two-pointer sweep skips the pairs it can prove are worse.
In an interview, start with brute force and name its wasted work. Then switch to two pointers and explain the shorter-wall rule. That climb from slow to fast is the story interviewers want to hear.
| 💡 Interview Insight If asked why the greedy pointer move is safe, tie it back to the formula. Width shrinks every step, so a wall can only help by being taller. Moving the shorter wall is the only move that can ever raise the short side. |
A few small traps catch beginners on this problem. Keep them in mind.
Run the tiny cases through your code before you call it done. They catch more bugs than any ordinary input will.
A: The width can only shrink as the pointers close in. So the only way to get a bigger area is a taller short wall. The taller wall never limits the water, which means moving it away wastes width for no gain. Moving the shorter wall is the only move that can raise the short side.
A: The brute force checks every pair of walls and runs in O(n squared) time. The two-pointer sweep needs only one pass and runs in O(n) time. Both use O(1) extra space, since they only track a handful of values.
A: No. The container using the shorter wall at its current width is already measured before we move. Any narrower container that still uses that same short wall can only be smaller. So we lose nothing by leaving the shorter wall behind.
A: Area equals the width times the shorter wall. The width is the gap between the two indexes. The shorter wall is the minimum of the two heights, because water spills over the lower side. In Java that is (higher – lower) * Math.min(height[lower], height[higher]).
Container With Most Water in Java looks hard at first, with all those walls to compare. But once you trust the shorter-wall rule, the fear melts away.
Our six-line trace showed the payoff clearly. Brute force ground through all fifteen pairs. The two-pointer sweep started wide, closed in, and found the same answer, 16, in only five stops.
So take the pattern, not just the answer. When width can only shrink, chase a taller short wall. Move the weak side and let the strong side wait.
That habit turns a slow square of work into a single clean pass. It will do the same for many array problems waiting further down the list.
javahandson.com | DSA Series | Arrays & Strings