Topic 2.9
Nested Loops and Patterns
In one line
A loop inside another loop runs its whole inner loop once for every iteration of the outer one. That's how you walk grids, compare every pair, and print patterns, and it's why the work grows as rows × columns (often n²).
Think of it like this
A clock. The minute hand goes all the way round, 60 steps, for each single step of the hour hand. In 12 hours the minute hand moves 12 × 60 = 720 times. Nested loops behave the same: the inner loop finishes all its rounds before the outer loop takes one step.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Nested loop
- A loop placed inside the body of another loop.
- Outer loop
- The enclosing loop. It moves one step at a time.
- Inner loop
- The loop inside. It runs completely for every single step of the outer loop.
- Row and column
- Positions in a grid: rows go down, columns go across.
- Pair
- Two different elements taken together, like (3, 8). n items have n(n-1)/2 unordered pairs.
- Time complexity
- How the amount of work grows as the input grows. Two nested loops over n items usually mean n² work, written O(n²).
- printf
- A print method that formats values with a template, like
%4dfor 'an integer in a field 4 characters wide'.
Step by step
01Watch the inner loop restart
Print the counters to see the order. For each value of i, j starts again at 1 and runs all the way to 3. The inner body runs 2 × 3 = 6 times.
for (int i = 1; i <= 2; i++) {
for (int j = 1; j <= 3; j++) {
System.out.println("i=" + i + " j=" + j);
}
}02Rows and columns: a grid
To print a grid, the outer loop handles rows, the inner loop prints each column with print (no newline), and a println() after the inner loop finishes the row.
printf("%4d", x) prints x right-aligned in 4 characters, so the columns line up. %n is a platform-independent newline.
for (int row = 1; row <= 3; row++) {
for (int col = 1; col <= 4; col++) {
System.out.printf("%4d", row * col);
}
System.out.println();
}03Patterns: turn the shape into a formula
Draw the shape, number the rows from 1, and write down for each row how many spaces and how many stars it has. Then find the rule.
For a pyramid of height 4: row 1 has 3 spaces and 1 star, row 2 has 2 spaces and 3 stars, row i has n - i spaces and 2 * i - 1 stars. Two inner loops, one per part, then println().
int n = 4;
for (int i = 1; i <= n; i++) {
for (int s = 0; s < n - i; s++) System.out.print(" ");
for (int k = 0; k < 2 * i - 1; k++) System.out.print("*");
System.out.println();
}04Inner bounds that depend on the outer variable
When the inner loop starts at i + 1, it visits every pair exactly once and never pairs an element with itself. For 4 items that's 6 pairs, not 16.
This 'every pair' loop is the brute-force solution to many interview questions (two-sum, closest pair, duplicates). The DSA course shows how hashing or sorting removes the inner loop.
String[] team = {"Ana", "Bob", "Cy", "Dee"};
for (int i = 0; i < team.length; i++) {
for (int j = i + 1; j < team.length; j++) {
System.out.println(team[i] + " vs " + team[j]);
}
} // 6 matches05How fast does the work grow?
Count the inner body runs. A full n × n nest runs n² times; the pair loop runs n(n-1)/2 times, about half, but still grows with n². Three nested loops would be n³.
At n = 1,000, n² is a million (fast); at n = 100,000 it's ten billion (seconds to minutes). That's why nested loops are fine for small inputs and the first thing to remove for big ones.
06The two classic nested-loop bugs
Wrong variable in the inner header: for (int j = 0; j < 3; i++) increments i, so j never changes and the inner loop never ends (or i races past the array and throws).
Inner counter not reset: declaring int j = 0; once, outside the outer loop, and writing for (; j < 3; j++) means j is already 3 when the second row starts, so only the first row is printed. Declare each counter in its own header and both bugs become hard to write.
int j = 0;
for (int i = 1; i <= 3; i++) {
for (; j < 3; j++) { // j is never reset
System.out.print("*");
}
System.out.println(" row " + i);
}Try it yourself
- 1
Flip the triangle
In 'Four classic patterns', make the right triangle upside down (4 stars first, 1 last) by changing only the inner loop's bound. Hint: row
ishould printn - i + 1stars. - 2
Predict the count
Add
n = 2000tosizesin 'Counting the work'. Predictfullandpairsbefore running (4,000,000 and 1,999,000). - 3
Make a diamond
Below the pyramid, add a second loop that counts
idown fromn - 1to 1 with the same body. Together they print a diamond.
Code & diagrams
Expected output
| 1 2 3 4 5
---+--------------------
1 | 1 2 3 4 5
2 | 2 4 6 8 10
3 | 3 6 9 12 15
4 | 4 8 12 16 20
5 | 5 10 15 20 25Expected output
Right triangle:
*
**
***
****
Pyramid:
*
***
*****
*******
Floyd's triangle:
1
2 3
4 5 6
7 8 9 10
Hollow square:
####
#..#
#..#
####Ten times more input means a hundred times more work: the signature of O(n²).
Expected output
n=10 full=100 pairs=45
n=100 full=10000 pairs=4950
n=1000 full=1000000 pairs=499500Expected output
i=1 j=1
i=2 j=1
i=3 j=1
The outer loop still ran 3 timesBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Declare the inner counter outside
Declare int j = 0; before the outer loop and use for (; j < 3; j++) inside.
public class Main {
public static void main(String[] args) {
int j = 0;
for (int i = 1; i <= 3; i++) {
for (; j < 3; j++) {
System.out.print("*");
}
System.out.println(" row " + i);
}
}
}Break #2
Increment the wrong counter
Write for (int j = 0; j < 3; i++) in the inner loop of a loop over an array with arr[i] in the body.
public class Main {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < 3; i++) {
System.out.println(arr[i]);
}
}
}
}Myth vs fact
Myth
Two nested loops always mean n² work.
Fact
Only if both run about n times. An inner loop with a fixed bound (say 26 letters) or one that shrinks quickly can make the total linear.
Myth
break in the inner loop stops everything.
Fact
It stops only the innermost loop. The outer loop carries on with its next iteration.
Myth
Pattern questions need a special trick per shape.
Fact
They all reduce to: for row i, how many spaces and how many symbols? Write the table, find the formula.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Loop order matters for memory. A Java
int[][]is an array of row arrays; iteratingfor row { for col }reads each row sequentially (cache-friendly), whilefor col { for row }jumps between separate arrays on every step and can be several times slower for large grids. - ▸
The JIT can hoist loop-invariant work out of the inner loop and remove bounds checks, but only when the inner loop is a simple counted loop. Calling a method in the inner condition (
j < list.size()) is usually fine after inlining; reading a non-final field may block hoisting. - ▸
Printing character by character with
System.out.printis slow:System.outis a synchronizedPrintStream, and each call takes a lock and may flush. For big outputs, build each line with aStringBuilder(or"*".repeat(k), Java 11) and print once per line. - ▸
Interviewers use nested loops to probe complexity: be ready to state the exact count (like n(n-1)/2) and then to remove a level with a hash set, sorting plus two pointers, or prefix sums.
Remember this
- 1
With an outer loop of
Riterations and an inner loop ofC, the inner body runs R × C times. For a square of size n that's n², so doubling n makes the work four times bigger. This is your first taste of time complexity, which the DSA course (/dsa) builds on. - 2
The usual reading for printing: the outer loop picks the row, the inner loop prints the columns of that row, and a
println()after the inner loop ends the line. Almost every pattern question is solved by working out, for rowi, how many spaces and how many symbols to print. - 3
The inner loop's bounds may depend on the outer variable:
for (int j = 0; j <= i; j++)prints a growing triangle, andfor (int j = i + 1; j < n; j++)visits each pair once (n(n-1)/2 times instead of n²). - 4
Use different names for each level (
i/j, orrow/col) and declare each counter in its own loop header. Two classic bugs: incrementing the wrong variable in the inner header (j < n; i++), and declaring the inner counter outside the outer loop so it never resets. - 5
A plain
breakorcontinueonly affects the innermost loop. To stop both loops, use a labelled break (Topic 2.8) or areturnfrom a method. - 6
Nested loops over a 2D array (Topic 3.9) should normally put the row index outside and the column inside. Java stores each row as a separate array, so walking along a row touches neighbouring memory, which the CPU cache rewards.
Explain it without notes
If the outer loop runs 5 times and the inner loop runs 4 times, how many times does the inner body run, and why?
How do you approach printing a pattern such as a pyramid?
Why does the inner loop in for (int j = i + 1; j < n; j++) produce each pair once, and how many iterations does it do?
Why should the inner counter be declared in the inner loop's header?
Practice
Print an inverted right triangle of height 4 using #.
Print the numbers 1 to 3 in a 3 × 3 grid where each row repeats its row number (1 1 1, 2 2 2, 3 3 3).
Given int[] a = {2, 7, 4, 7, 2, 9}, use nested loops to print every pair of positions that hold equal values.
Trade-offs
- ↔
Nested loops are the simplest correct solution for grids and pairs, but O(n²) work becomes too slow somewhere between thousands and millions of items; then use hashing, sorting or a smarter algorithm.
- ↔
Printing patterns character by character is easy to reason about; building each line with
repeator aStringBuilderis faster and shorter but hides the per-character logic beginners need to see.
Done when you can
Done when you can trace a nested loop and state how many times the inner body runs.
Done when you can print a triangle, pyramid and hollow square from a row/space/symbol table.
Done when you can write the every-pair loop with
j = i + 1and give its iteration count.Done when you can spot an inner counter that isn't reset or a wrong variable in the inner header.
Done when you can explain why row-then-column order is faster for 2D arrays.