Pattern 18
Java source ↗ Edit this article ↗
Jump to code:
Problem
Given a positive integer n, print the alpha-triangle: n rows that all end with the n-th capital letter. Row i (counting from 1) contains i letters, the last i letters of A .. (n-th letter), separated by single spaces. For n = 5 the rows are E, D E, C D E, B C D E, A B C D E.
Constraints: 1 ≤ n ≤ 26, because there are 26 letters.
Example 1
n = 3
C
B C
A B C
The right end is anchored at C; each row extends one letter further to the left.
Example 2
n = 5
E
D E
C D E
B C D E
A B C D E
The last row is the full alphabet prefix of length n.
Reading the pattern
The shape is the usual growing triangle: row i has i entries. The content, however, is anchored on the right. Using 0-based letter indices (A = 0), row i runs from index n - i up to n - 1. Check: row 1 is just index n - 1 (the n-th letter), and row n is indices 0 .. n - 1, the whole prefix.
So, compared with the increasing letter triangle A / A B / A B C, only the start of the inner loop changes: it begins at n - i instead of 0. Getting comfortable with “same loop shape, shifted start” is the main skill this pattern trains.
A second view: every row is the previous row with one more letter glued on the front. If you keep the current row in a buffer and prepend the next letter (and a space), you never recompute anything. That is the mirror image of the “append one more letter” idea used in growing patterns.
Both approaches return the whole pattern as one String, one row per line, so main can compare them against fixed expectations and structural checks before printing.
Approach 1: Nested loops with a shifted start
Intuition. Outer loop over rows; inner loop over letter indices j from n - i to n - 1. Append a space before every letter except the first of the row, which is the one at j == n - i.
Algorithm.
- For
ifrom1ton: - For
jfromn - iton - 1: ifj > n - iappend a space; append(char) ('A' + j). - End the row with a newline.
static String nestedLoops(int n) {
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= n; i++) {
for (int j = n - i; j < n; j++) {
if (j > n - i) sb.append(' ');
sb.append((char) ('A' + j));
}
sb.append('\n');
}
return sb.toString();
}
Complexity. Time O(n^2): n(n+1)/2 letters and n(n-1)/2 spaces. Space O(n^2) for the output string, with constant extra memory.
Why (char) ('A' + j) and not 'A' + j? The sum of a char and an int is an int; append(int) would print numbers such as 69. The cast turns the code point back into a character.
Approach 2: Grow the row leftwards
Intuition. Row i is row i - 1 with the letter (char) ('A' + n - i) and a separating space placed in front. Keep the current row in a StringBuilder and use insert(0, ...) to prepend. Each row is then emitted with a single append.
Algorithm.
- Keep
row = "". - For
ifrom1ton: letch = (char) ('A' + n - i). Ifi > 1, insert a space at position 0. Insertchat position 0. - Append
rowand a newline.
static String growLeft(int n) {
StringBuilder sb = new StringBuilder();
StringBuilder row = new StringBuilder();
for (int i = 1; i <= n; i++) {
char ch = (char) ('A' + n - i);
if (i > 1) row.insert(0, ' ');
row.insert(0, ch);
sb.append(row).append('\n');
}
return sb.toString();
}
Dry run for n = 4:
| i | ch | row after prepending | printed |
|---|---|---|---|
| 1 | D |
D |
D |
| 2 | C |
C D |
C D |
| 3 | B |
B C D |
B C D |
| 4 | A |
A B C D |
A B C D |
Complexity. Time O(n^2): insert(0, ...) shifts the existing characters, which costs O(i) for row i, and copying the row into the output costs the same. Space O(n^2) for the output plus O(n) for the working row.
The order of the two inserts matters. Insert the space first and then the letter so that the letter ends up at index 0 and the space between it and the old content. Doing it the other way round yields " CD" with a leading space.
Edge cases and pitfalls
n = 0. Empty output. Both outer loops do not execute.n = 1. A singleA:n - i = 0for the only row, so the first and last letters coincide.- Wrong anchor. Starting the inner loop at
0instead ofn - igives the increasing letter triangle. Starting ati - 1gives a triangle that ends at different letters. Verify with row 1, which must be exactly then-th letter. - Separator placement. The condition
j > n - iputs a space before every letter except the row’s first. Writingj > 0would be wrong here, because the row’s first letter has indexn - i, not0. - Trailing spaces. As in all patterns in this series, no trailing whitespace is produced; the tests assert each row is exactly
2i - 1characters wide. n > 26.'A' + n - 1is pastZ; the loops still work, but the symbols are not letters.insertinto aStringBuilderis linear. For a pattern it does not matter, but if you ever prepend to a very long buffer in a hot loop, build the content reversed and flip it once at the end instead.
Related problems
- Pattern 14 is the same triangle anchored on the left (
A / A B / A B C). - Pattern 15 also shrinks or grows by one letter per row, from the other end.
- Pattern 16 repeats a single row-dependent letter.
- Pattern 12 uses the same prepend trick to build the reversed half of a number row.