Nested for Loops: Thinking in Rows and Columns

Some problems are naturally two-dimensional: printing grids, building tables, scanning pairs \((i,j)\), summing over regions like \(1 \le j \le i \le N\). A nested loop is just a loop inside another loop—perfect for “for each row, for each column” patterns. The key mental model:

  1. The outer loop picks a row (runs slowly).
  2. For each row, the inner loop runs to completion (runs fast).
  3. After the inner loop finishes, control returns to the outer loop for the next row.

Once you see nested loops as row × column, the control flow becomes predictable.

Example 1 — A “visual” triangle of symbols

Goal: print a left-aligned triangle of * with height N.

*
**
***
****
#include <iostream>

int main() {
    int N;
    std::cout << "Triangle height N: ";
    std::cin >> N;

    for (int row = 1; row <= N; row = row + 1) {
        // Print 'row' stars on this line
        for (int k = 1; k <= row; k = k + 1) {
            std::cout << '*';
        }
        std::cout << '\n'; // move to the next line
    }

    return 0;
}

How it runs:

  • row = 1: inner loop prints 1 star → newline
  • row = 2: inner loop prints 2 stars → newline
    … up to row = N.

Variant: right-aligned triangle (spaces + stars)

   *
  **
 ***
****
#include <iostream>

int main() {
    int N;
    std::cout << "Triangle height N: ";
    std::cin >> N;

    for (int row = 1; row <= N; row = row + 1) {
        // First, N-row spaces
        for (int s = 1; s <= N - row; s = s + 1) {
            std::cout << ' ';
        }
        // Then, row stars
        for (int k = 1; k <= row; k = k + 1) {
            std::cout << '*';
        }
        std::cout << '\n';
    }

    return 0;
}

Two inner loops make the “shape” obvious: spaces, then stars.

Example 2 — Multiplication table (1…N) as a grid

We build an \(N\times N\) grid where the entry in row \(i\), column \(j\) is \(i \times j\).

#include <iostream>

int main() {
    int N;
    std::cout << "Table size N: ";
    std::cin >> N;

    // Optional header row
    std::cout << "   ";                // small left margin
    for (int j = 1; j <= N; j = j + 1) {
        std::cout << j << '\t';
    }
    std::cout << '\n';

    for (int i = 1; i <= N; i = i + 1) {
        std::cout << i << " | ";       // row label
        for (int j = 1; j <= N; j = j + 1) {
            std::cout << (i * j) << '\t';
        }
        std::cout << '\n';
    }

    return 0;
}

We use '\t' (a tab) for simple column separation—no extra libraries needed.

Example 3 — Double sums: full square vs. triangle

Sum over all pairs \((i,j)\) in a square \(1\le i,j\le N\)

#include <iostream>

int main() {
    int N;
    std::cout << "N for double sum: ";
    std::cin >> N;

    long long S = 0; // use a wider type for safety
    for (int i = 1; i <= N; i = i + 1) {
        for (int j = 1; j <= N; j = j + 1) {
            S = S + (i * j);
        }
    }

    std::cout << "Sum_{i=1..N} Sum_{j=1..N} (i*j) = " << S << '\n';
    return 0;
}

Sum over a triangle \(1\le j\le i\le N\) (avoid double counting)

#include <iostream>

int main() {
    int N;
    std::cout << "N for triangular sum: ";
    std::cin >> N;

    long long T = 0;
    for (int i = 1; i <= N; i = i + 1) {
        for (int j = 1; j <= i; j = j + 1) { // note j <= i
            T = T + (i + j);
        }
    }

    std::cout << "Sum over 1<=j<=i<=N of (i+j) = " << T << '\n';
    return 0;
}

By changing the inner loop’s check from j <= N to j <= i, we restrict to the lower triangle.

Example 4 — Counting lattice points in a disk

Count integer pairs \((x,y)\) with \(-R \le x,y \le R\) that satisfy \(x^2 + y^2 \le R^2\).

#include <iostream>

int main() {
    int R;
    std::cout << "Radius R: ";
    std::cin >> R;

    int count = 0;
    for (int x = -R; x <= R; x = x + 1) {
        for (int y = -R; y <= R; y = y + 1) {
            if (x*x + y*y <= R*R) {
                count = count + 1;
            }
        }
    }

    std::cout << "Lattice points with x^2 + y^2 <= R^2: " << count << '\n';
    return 0;
}

Mathematically, this explores a 2D region using a rectangular scan and an if to test membership.

Reading the flow (what runs when?)

For the classic form

for (int i = 1; i <= A; i = i + 1) {
    for (int j = 1; j <= B; j = j + 1) {
        // body
    }
}

the order is:

  1. i = 1, then j runs 1..B completely
  2. i = 2, then j runs 1..B completely
    … up to i = A.

The inner loop resets each time the outer loop advances.

Common pitfalls (and fixes)

  • Using the same variable name for both loops.
    • Fix: use distinct names (row/col, i/j).
  • Forgetting the newline after finishing a row in “visual” problems.
    • Fix: print '\n' once per row, after the inner loop completes.
  • Off-by-one in triangular regions (j < i vs j <= i).
    • Fix: write down a tiny case (e.g., N = 3) and list the pairs to confirm.
  • Performance surprises: nested loops over N go like (N^2).
    • Fix: start with small N to test, then scale up.

Key takeaways

  • A nested loop is a natural way to model row × column or pairwise work.
  • The inner loop runs to completion for each outer iteration.
  • Shapes (rectangle vs. triangle) are controlled by the inner loop’s bounds and simple if tests.
  • Visual tasks (patterns) and mathematical tasks (tables, sums, regions) both become straightforward when you think in terms of rows and columns.