Pattern 15
Java source ↗ Edit this article ↗
Jump to code:
Problem
Given a positive integer n, print the reverse letter triangle: n rows where the first row contains the first n capital letters A B C ... separated by single spaces, and each following row drops the last letter, until the final row is just A.
Constraints: 1 ≤ n ≤ 26, because there are 26 letters.
Example 1
n = 3
A B C
A B
A
Each row is the previous row without its last letter.
Example 2
n = 5
A B C D E
A B C D
A B C
A B
A
Five rows, the first with five letters, the last with one.
Reading the pattern
Row i (1-based) contains n - i + 1 letters, always starting at A. So the shape is an inverted right-angled triangle, and the content in column j (0-based) is (char) ('A' + j) just like the increasing version. The only difference from the growing triangle is the inner loop bound: it is n - i + 1 instead of i.
There is a second way to see it. The first row is the longest; every later row is the one above with two characters chopped off the end (a space and a letter). If you keep the current row in a mutable buffer, printing the pattern is “print, shrink by two, repeat”. This viewpoint avoids recomputing the letters of each row and is the natural way to think about patterns whose rows are suffix- or prefix-related.
Both approaches build the whole output as one String, one row per line, so main can compare against expected strings and structural properties before printing.
Approach 1: Nested loops with a shrinking bound
Intuition. Same skeleton as any triangle: an outer loop over rows and an inner loop over columns. Compute the number of letters of this row first, letters = n - i + 1, and let the inner loop run that many times. Prefix every letter except the first with a space.
Algorithm.
- For
ifrom1ton: letletters = n - i + 1. - For
jfrom0toletters - 1: ifj > 0append 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++) {
int letters = n - i + 1;
for (int j = 0; j < letters; j++) {
if (j > 0) sb.append(' ');
sb.append((char) ('A' + j));
}
sb.append('\n');
}
return sb.toString();
}
Complexity. Time O(n^2): n + (n-1) + ... + 1 = n(n+1)/2 letters plus the spaces between them. Space O(n^2) for the output; nothing else grows with n.
Approach 2: Build the first row and shrink it
Intuition. The first row, A B C ... with n letters, is the only one that needs letters computed. Every subsequent row is obtained by deleting its last two characters: the final letter and the space before it. StringBuilder.setLength(len - 2) does that deletion in O(1), since it just moves the end marker. Append the buffer, shrink, repeat.
Algorithm.
- Build
row=A B ...withnletters (length2n - 1). - For
ifrom1ton: appendrowand a newline; ifrowhas at least two characters, drop the last two.
static String shrinking(int n) {
StringBuilder row = new StringBuilder(); // "A B C ... " with n letters
for (int j = 0; j < n; j++) {
if (j > 0) row.append(' ');
row.append((char) ('A' + j));
}
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= n; i++) {
sb.append(row).append('\n');
if (row.length() >= 2) row.setLength(row.length() - 2); // drop " X"
}
return sb.toString();
}
Dry run for n = 4:
| i | row before printing | printed | row after shrink |
|---|---|---|---|
| 1 | A B C D |
A B C D |
A B C |
| 2 | A B C |
A B C |
A B |
| 3 | A B |
A B |
A |
| 4 | A |
A |
A (length 1, no shrink) |
Complexity. Time O(n^2): building the first row is O(n), each append(row) copies the current row, and the copies total (2n-1) + (2n-3) + ... + 1 = n^2 characters. Space O(n^2) for the output plus O(n) for the working row.
Why remove two characters? With single-space separators each letter after the first occupies two characters (" X"). Removing one character would leave a trailing space on the next row; removing three would eat a letter too many. The guard row.length() >= 2 protects the last iteration, where the row is just A and setLength(-1) would throw.
Edge cases and pitfalls
n = 0. Empty output. Approach 2 builds an emptyrowand never enters the printing loop.n = 1. A singleA. Both the shrinking guard and the loop boundletters = 1handle it.- Off-by-one in the bound.
letters = n - i(without+ 1) printsn - 1letters in the first row and an empty last row. Check the first and last rows by hand: row 1 must havenletters, rownmust have one. - Trailing spaces. The separator is placed before every letter except the first. The tests assert each row’s length is exactly
2 · letters - 1. - Casting.
'A' + jis anint; append it as(char) ('A' + j)or you print numbers such as65 66 67. n > 26. Characters afterZare[,\,], … and are not letters. Validate the input or cycle with% 26.setLengthvsdeleteCharAt.deleteCharAttwice works too, butsetLengthexpresses the intent (“truncate”) and isO(1)because nothing is shifted.
Related problems
- Pattern 14 is the same triangle growing instead of shrinking.
- Pattern 18 also ends every row at a fixed letter, but keeps the alphabet’s right end instead of its left end.
- Pattern 16 uses one repeated letter per row.
- Pattern 12 is a numeric pattern where the inner loop bounds also change with the row.