Topics › Basics › Patterns

Pattern 3

Easy TUF ID 416 A2Z

Problem

Given an integer n, print a right-angled number pyramid: n rows, where row i (counting from 1) contains the numbers 1, 2, ..., i separated by single spaces. The shape is the triangle of Pattern 2, but each star is replaced by its column number.

Output format: every row ends with a newline, numbers in a row are separated by exactly one space, and there is no trailing space. Numbers with two or more digits are printed in full (10, 11, …); there is no padding. n = 0 prints nothing.

Constraints: 0 ≤ n ≤ 100 is typical.

Example 1

n = 4

1
1 2
1 2 3
1 2 3 4

Row i counts from 1 up to i.

Example 2

n = 1

1

A single number, so there is no separator.

What changes compared to the star triangle

The loop structure is the triangle’s: outer loop over rows, inner loop from 1 to i. The new element is the cell content. Instead of a constant *, we print the inner loop counter j. This is the first pattern where the value printed is data derived from the indices rather than a fixed symbol, and the lesson generalises: once the loop skeleton is right, any pattern is just a formula from (i, j) to a character or number.

In Java there is a small trap here. StringBuilder.append(int) appends the decimal representation, so sb.append(j) writes "12" for j = 12. If you cast to char or add to '0' to “convert” a digit, you get a single character, which is wrong for j ≥ 10 and silently produces garbage. Keep numbers as int and let append or Integer.toString do the formatting.

All approaches return the pattern as a String so the tests can compare it with expected literals.

Approach 1: Nested loops

Intuition. Row i has i cells and cell j holds the value j. The counter is the content, so the body is just append(j), preceded by a space when j > 1.

Algorithm.

  1. For i from 1 to n:
  2. For j from 1 to i: if j > 1 append a space; append the number j.
  3. Append \n.
  4. Return the string.
static String nestedLoops(int n) {
    StringBuilder sb = new StringBuilder();
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= i; j++) {
            if (j > 1) sb.append(' ');
            sb.append(j);
        }
        sb.append('\n');
    }
    return sb.toString();
}

Complexity. Time O(n^2): n(n+1)/2 numbers are written, each with O(log n) digits, which is a constant for any practical n. Space O(n^2) for the output; O(1) extra.

Check the boundary rows before moving on. For i = 1 the inner loop runs once and writes "1" with no separator; for i = n it writes 1 through n. If the first row comes out empty, the inner loop started at j = 1 but the outer loop started at i = 0; if the last row stops at n - 1, the inner bound was j < i instead of j <= i. Testing these two rows catches almost every off-by-one error in this family of patterns.

Approach 2: Grow one row incrementally

Intuition. Row i is row i - 1 with " i" appended: "1", "1 2", "1 2 3". So maintain a single row buffer, extend it by one number per iteration, and copy it into the output. The inner loop vanishes and the dependency on i lives in the carried state. This is also the natural way to answer “print only the k-th row” in O(k) without recomputing anything.

Algorithm.

  1. Keep out (result) and row (current row) builders.
  2. For i from 1 to n: if i > 1 append a space to row; append the number i to row; append row then \n to out.
  3. Return out.
static String growingRow(int n) {
    StringBuilder out = new StringBuilder();
    StringBuilder row = new StringBuilder();
    for (int i = 1; i <= n; i++) {
        if (i > 1) row.append(' ');
        row.append(i);
        out.append(row).append('\n');
    }
    return out.toString();
}

Complexity. Time O(n^2) because copying row costs O(i) at step i. Space O(n) extra for the row buffer.

Approach 3: Streams

Intuition. A row is “the numbers 1..i joined by spaces”. IntStream.rangeClosed(1, i) gives the numbers, mapToObj(j -> Integer.toString(j)) turns them into strings, and Collectors.joining(" ") glues them without a trailing space. An outer stream over the row indices produces and concatenates the rows. The code is a direct transcription of the definition.

Algorithm.

  1. Outer IntStream.rangeClosed(1, n) over rows.
  2. For each i, inner IntStream.rangeClosed(1, i) mapped to strings and joined with " ", followed by \n.
  3. Join all rows with Collectors.joining().
static String streams(int n) {
    return IntStream.rangeClosed(1, n)
            .mapToObj(i -> IntStream.rangeClosed(1, i)
                    .mapToObj(j -> Integer.toString(j))
                    .collect(Collectors.joining(" ")) + "\n")
            .collect(Collectors.joining());
}

Dry run for n = 3:

i inner stream row
1 1 "1\n"
2 1, 2 "1 2\n"
3 1, 2, 3 "1 2 3\n"

Result: "1\n1 2\n1 2 3\n".

Complexity. Time O(n^2), space O(n) extra per row. For n = 0 the outer range is empty and the result is "".

Edge cases and pitfalls