Pattern 14
Java source ↗ Edit this article ↗
Jump to code:
Problem
Given a positive integer n, print the increasing letter triangle: n rows where row i (counting from 1) contains the first i capital letters of the alphabet, A to the i-th letter, separated by single spaces.
Constraints: 1 ≤ n ≤ 26, since there are only 26 letters. The code does not crash for larger n, but the characters after Z are no longer letters.
Example 1
n = 3
A
A B
A B C
Every row starts over at A and goes one letter further than the row above.
Example 2
n = 5
A
A B
A B C
A B C D
A B C D E
The last row contains the first five letters.
Reading the pattern
This pattern is the letter version of the number pyramid 1 / 1 2 / 1 2 3. The shape is the usual right-angled triangle, for i in 1..n, for j in 0..i-1, and the content in column j (0-based) is the letter 'A' + j.
The one new ingredient is character arithmetic. In Java a char is a 16-bit unsigned integer holding a Unicode code point. 'A' is 65, 'B' is 66, and so on through 'Z' = 90. When you write 'A' + j, both operands are promoted to int and the result is an int; to append it as a letter you must cast it back with (char) ('A' + j). Without the cast, sb.append('A' + 2) appends the text "67", not C.
There is also a useful structural observation: every row is a prefix of the last row. Row i is simply the first 2i - 1 characters of A B C D E ... (i letters plus i - 1 spaces). That gives a second, different way to produce the output.
Both approaches return the pattern as one String with a newline after every row, so main can compare them against expected strings before printing.
Approach 1: Nested loops with char arithmetic
Intuition. Walk the triangle cell by cell. The row index decides how many letters to print; the column index decides which letter. Put a space before every letter except the first so that no row ends with trailing whitespace.
Algorithm.
- For
ifrom1ton: - For
jfrom0toi - 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++) {
for (int j = 0; j < i; j++) {
if (j > 0) sb.append(' ');
sb.append((char) ('A' + j));
}
sb.append('\n');
}
return sb.toString();
}
Complexity. Time O(n^2): the triangle has n(n+1)/2 letters and almost as many spaces, each appended in O(1). Space O(n^2) for the output string; no other memory grows with n.
Approach 2: Every row is a prefix of the last row
Intuition. Build the longest row, A B C ... with n letters, exactly once. Then row i is that string cut after 2i - 1 characters: i letters interleaved with i - 1 spaces. StringBuilder.append(CharSequence, start, end) copies that range without creating an intermediate String.
This is a general trick for “growing” patterns whose rows share a common start: compute the largest row once and slice it.
Algorithm.
- Build
full= the lettersA .. (n-th letter)separated by spaces; its length is2n - 1. - For
ifrom1ton: appendfull[0, 2i - 1)and a newline.
static String prefixOfLastRow(int n) {
StringBuilder full = new StringBuilder(); // "A B C ... " with n letters
for (int j = 0; j < n; j++) {
if (j > 0) full.append(' ');
full.append((char) ('A' + j));
}
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= n; i++) {
sb.append(full, 0, 2 * i - 1).append('\n'); // i letters and i-1 spaces
}
return sb.toString();
}
Dry run for n = 4, where full = "A B C D" (length 7):
| i | prefix length 2i-1 | row |
|---|---|---|
| 1 | 1 | A |
| 2 | 3 | A B |
| 3 | 5 | A B C |
| 4 | 7 | A B C D |
Complexity. Time O(n^2): building full is O(n), and the copies total 1 + 3 + 5 + ... + (2n-1) = n^2 characters. Space O(n^2) for the output plus O(n) for full.
Why is the prefix length 2i - 1 and not 2i? Each letter except the last is followed by one space, so i letters use i + (i - 1) characters. If you used 2i you would copy the space after the last letter and reintroduce trailing whitespace.
Edge cases and pitfalls
n = 0. Empty output. In Approach 2,fullis empty and the row loop does not run, so nothing is sliced out of bounds.n = 1. One row,A. The prefix length is2 · 1 - 1 = 1.- Forgetting the cast.
sb.append('A' + j)compiles (the argument is anint) and appends digits like"65". Always writesb.append((char) ('A' + j)). The cast binds tighter than+, so(char) 'A' + jis also wrong: it casts'A'first and then adds, producing anintagain. n > 26.'A' + 26is'[', the character afterZin the Unicode table. The program still runs and both approaches agree, but the output is no longer letters. Validaten ≤ 26if the input can be arbitrary, or wrap with(char) ('A' + j % 26)if cycling is acceptable.- Trailing spaces. Both approaches put the separator before every letter except the first. The tests check each row’s length is exactly
2i - 1, which would fail with a trailing space. - Lowercase variant. Replace
'A'with'a'; nothing else changes because'a'..'z'are also consecutive code points (97..122). Character.getNumericValueis not what you want here. That method converts digit characters to their numeric value; for letters it returns10..35and is unrelated to alphabet position. Alphabet position isch - 'A'.
Related problems
- Pattern 15 is this triangle upside down: the rows shrink from
nletters to one. - Pattern 16 keeps the shape but repeats a single letter per row.
- Pattern 18 grows the rows from the right end of the alphabet instead of the left.
- Pattern 13 is the same shape with a running count of numbers.