Container With Most Water in Java DSA: The Two-Pointer Trick That Beats Every Loop

  • Last Updated: July 28, 2026
  • By: javahandson
  • Series
img

Container With Most Water in Java DSA: The Two-Pointer Trick That Beats Every Loop

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.

1. Introduction

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.

2. Understanding the Problem

Let us nail down the rules before we write any code.

  • You get an array called height, like [1, 8, 6, 2, 5, 4]. Each number is the height of one vertical line.
  • The index of each line is its position on the x-axis. So the distance between two lines is just the gap between their indexes.
  • Two lines form a container. Water fills the space between them.
  • The water height equals the shorter of the two lines, because the taller side cannot hold extra water.

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.

3. Concepts You Need Here

3.1 Area Is Width Times the Shorter Wall

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.

3.2 The Two-Pointer Idea

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.

  • Call the left pointer lower and the right pointer higher.
  • At each stop, measure the container between them.
  • Then move the pointer that sits on the shorter wall, because only a taller wall can help you.

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.

4. Approach 1: Brute Force

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.

4.1 Pseudocode

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 best

4.2 Pseudocode Explained

  • The outer loop picks the left wall, the inner loop picks a right wall to its right.
  • For each pair, width is the gap and wall is the shorter of the two heights.
  • Multiply them for the area, then keep it if it beats the best so far.

4.3 Java Code

public 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
    }
}

4.4 Java Code Explained

  • Line 4 starts best at 0, since no container has been measured yet.
  • Then lines 5 and 6 run the two loops that pick every left-right pair.
  • Next, line 7 finds the width and line 8 finds the shorter wall with Math.min.
  • Line 9 multiplies them, and lines 10 to 12 save the area when it beats best.

4.5 Dry Run of the Brute Force

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

4.6 Reading the Dry Run

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.

  • At step 1, j is on 8, so the shorter wall is still 1 and the width is 1. Area is 1, our first best.
  • Then steps 2 to 5 push j further right, so the width grows to 2, 3, 4, and 5.
  • But the shorter wall stays stuck at 1 the whole time, because line 0 is only 1 tall.

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.

  • At step 6, j is on 6, width 1, shorter wall 6, area 6. That already ties our running best.
  • During step 7, j moves to the height-2 line, so the wall drops to 2 and area falls to 4. No new best.
  • By step 8, j reaches 5, width 3, wall 5, area 15. A big jump and a new best.
  • At step 9, j lands on 4, width 4, wall 4, area 16. This is the winner of the whole array.

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.

  • Steps 10 to 12 fix i on the height-6 line, giving areas 2, 10, and 12. Close, but under 16.
  • During steps 13 and 14, i sits on the height-2 line, so the walls stay small and areas stay tiny.
  • Finally step 15 checks the last pair, 5 and 4, for an area of 4.

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.

4.7 Time and Space Cost

  • Time is O(n squared), because of the two nested loops over the array.
  • Space is O(1), since we only keep a single best value.

Fifteen pairs for six lines is nothing. For a few thousand lines it crawls, which is why we sharpen it next.

5. Approach 2: Two-Pointer Sweep

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.

5.1 Pseudocode

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 best

5.2 Pseudocode Explained

  • lower starts at the far left and higher starts at the far right, the widest gap.
  • At each stop, measure the container and update best if it is larger.
  • Then move whichever pointer sits on the shorter wall, hunting for a taller one.

5.3 Java Code

public 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
    }
}

5.4 Java Code Explained

  • Line 4 sets lower at the start and higher at the end of the array.
  • The loop on line 6 keeps going while the two pointers have not met.
  • Next, lines 7 to 9 measure the current container and keep the larger area.
  • Lines 10 to 14 move whichever pointer holds the shorter wall inward by one.

5.5 Dry Run of the Two-Pointer Sweep

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.

5.6 Reading the Dry Run

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.

  • Area is 5 times 1, which is 5. That becomes our first best.
  • Since 1 is less than 4, the left wall is shorter, so lower steps right to index 1.

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.

  • The shorter wall is now 4, so the area is 4 times 4, which is 16. A new best, and in fact the final answer.
  • Because 8 is greater than 4, the right wall is shorter, so higher steps left to index 4.

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.

  • Area is 3 times 5, which is 15. Close, but it does not beat 16, so best stays put.
  • Since 8 is still greater than 5, higher steps left again to index 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.

  • Area is 2 times 2, which is 4. Far below 16, so nothing changes.
  • Because 8 beats 2, higher steps left one more time to index 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.

  • Area is 1 times 6, which is 6. Still under 16, so best holds.
  • Since 8 is greater than 6, higher steps left to index 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.

5.7 Time and Space Cost

  • Time is O(n), because each pointer moves inward at most n steps in total.
  • Space is O(1), since we only track two indexes and one best value.

This is a clean jump from O(n squared) down to O(n). Same answer, far less work, and almost no memory.

6. The Dry Run on Paper

Tables are exact, but a sketch often lands faster. Here is the same two-pointer sweep drawn by hand.

Container With Most Water two-pointer approach in Java dsa
  • The bars show each line, with the winning container shaded across index 1 and index 5.
  • Each row lists a pointer stop, its area, and which pointer moves next.
  • At the bottom, the finished answer reads 16.

7. Comparing the Two Approaches

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.

8. Common Mistakes and Edge Cases

A few small traps catch beginners on this problem. Keep them in mind.

  • Using the taller wall as the water height is wrong. Water spills over the shorter side, so always take the minimum.
  • Moving the taller pointer wastes width and can miss the best answer. Always move the shorter one.
  • Forgetting the loop guard lower < higher can make the pointers cross and read bad indexes.
  • An array with fewer than two lines holds no water, so the answer is 0.
  • When the two walls are equal, moving either pointer is fine. The code just picks the higher side by default.

Run the tiny cases through your code before you call it done. They catch more bugs than any ordinary input will.

9. Interview Questions

Q: Why do we always move the shorter wall in the two-pointer approach?

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.

Q: What is the time complexity of Container With Most Water in Java?

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.

Q: Can moving the shorter pointer skip the real answer?

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.

Q: How is the water area calculated?

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]).

10. Conclusion

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.

11. Further Reading

  • LeetCode Container With Most Water problem — practice the original and submit your own solution: https://leetcode.com/problems/container-with-most-water/
  • Oracle Java Math documentation — the official reference for Math.min and Math.max used here: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Math.html
  • Previous in this series: 3Sum in Java https://javahandson.com/3sum-in-java/ — fix one number, then two pointers sweep for the pair.
  • Next in this series: Product of Array Except Self in Java https://javahandson.com/product-of-array-except-self-in-java/ — build the answer with prefix and suffix passes, no division.

javahandson.com | DSA Series | Arrays & Strings

Leave a Comment