Pattern 20
Java source ↗ Edit this article ↗
Jump to code:
Problem
Given a positive integer n, print the symmetric butterfly: 2n - 1 rows, each 2n characters wide. Row r (counting from 1) has s stars at the left edge and s stars at the right edge with spaces in between, where s grows 1, 2, ..., n over the first n rows and then shrinks n - 1, ..., 1 over the remaining n - 1 rows. The middle row is solid.
Constraints: 1 ≤ n ≤ 50 or so; the output has about 4n^2 characters.
Example 1
n = 3
* *
** **
******
** **
* *
Five rows; the third is solid and the figure is mirrored around it.
Example 2
n = 5
* *
** **
*** ***
**** ****
**********
**** ****
*** ***
** **
* *
Nine rows of ten characters; the wings meet in row 5.
Reading the pattern
Each row is s stars, a gap, and s stars again, with a fixed width of 2n, so the gap is 2n - 2s = 2(n - s). As with every “two sides and a gap” figure, the row is fully described by s, and the pattern is the sequence of star counts
1, 2, ..., n-1, n, n-1, ..., 2, 1
for rows r = 1 .. 2n - 1. The sequence rises to n at the middle row r = n and falls symmetrically, which is exactly s = n - |n - r|: the distance of r from the middle, subtracted from the peak.
Two implementations follow. The first writes two loops, one per wing, mirroring each other. The second uses the absolute-value formula to handle both wings in a single loop. Both return the finished picture as one String with a newline after every row, so main can compare them against expected strings and structural properties (palindromic rows, vertical mirror symmetry, a solid middle row) for n up to 20.
Approach 1: Two wings, three loops per row
Intuition. The upper wing has rows with i = 1 .. n stars per side; the lower wing has rows with i = n - 1 .. 1 stars per side. Both wings use the same row recipe: i stars, 2(n - i) spaces, i stars. So write the recipe twice, once under a rising loop and once under a falling loop.
Algorithm.
- For
ifrom1ton: appendistars,2(n - i)spaces,istars, newline. - For
ifromn - 1down to1: appendistars,2(n - i)spaces,istars, newline.
static String nestedLoops(int n) {
StringBuilder sb = new StringBuilder();
for (int i = 1; i <= n; i++) { // upper wing, i stars each side
for (int j = 0; j < i; j++) sb.append('*');
for (int j = 0; j < 2 * (n - i); j++) sb.append(' ');
for (int j = 0; j < i; j++) sb.append('*');
sb.append('\n');
}
for (int i = n - 1; i >= 1; i--) { // lower wing, i stars each side
for (int j = 0; j < i; j++) sb.append('*');
for (int j = 0; j < 2 * (n - i); j++) sb.append(' ');
for (int j = 0; j < i; j++) sb.append('*');
sb.append('\n');
}
return sb.toString();
}
Complexity. Time O(n^2): 2n - 1 rows of 2n characters each. Space O(n^2) for the output; constant extra memory.
The lower loop starts at n - 1, not n, so the solid row is printed exactly once. Starting it at n produces two solid rows and 2n rows in total, which is a different figure.
Approach 2: One loop with s = n - |n - r|
Intuition. The star count rises linearly to the middle row and falls linearly after it. “Distance from the middle” is |n - r|, and the count is the peak minus that distance. One loop over r = 1 .. 2n - 1 with this formula removes the duplicated recipe, and String.repeat builds each row from three pieces.
Algorithm.
- For
rfrom1to2n - 1: lets = n - |n - r|. - Let
side = "*".repeat(s). - Append
side," ".repeat(2(n - s)),side, newline.
static String formula(int n) {
StringBuilder sb = new StringBuilder();
for (int r = 1; r <= 2 * n - 1; r++) {
int stars = n - Math.abs(n - r); // 1, 2, ..., n, ..., 2, 1
String side = "*".repeat(stars);
sb.append(side).append(" ".repeat(2 * (n - stars))).append(side).append('\n');
}
return sb.toString();
}
Dry run for n = 3:
| r | n - r | stars | gap | row |
|---|---|---|---|---|
| 1 | 2 | 1 | 4 | * * |
| 2 | 1 | 2 | 2 | ** ** |
| 3 | 0 | 3 | 0 | ****** |
| 4 | -1 | 2 | 2 | ** ** |
| 5 | -2 | 1 | 4 | * * |
Complexity. Time O(n^2): three repeat calls per row with total length 2n. Space O(n^2) for the output plus O(n) of short-lived temporaries per row.
The formula view also explains the symmetry directly: rows r and 2n - r have |n - r| and |n - (2n - r)| = |r - n| respectively, the same distance, hence the same row. The tests assert rows[r] == rows[2n - 2 - r] (0-based) for every n.
Edge cases and pitfalls
n = 0. Zero rows; both methods return the empty string. In Approach 2 the loop bound2n - 1 = -1means the loop body never runs.n = 1. One row,**: the single row is both the first wing row and the middle row.- Middle row counted twice. The lower wing must start at
n - 1. This is the most common bug in this pattern and shows up as a figure with2nrows. - Gap width. Each side loses or gains one star per row, so the gap changes by two. Writing
n - ispaces instead of2(n - i)makes the rows uneven. - Odd number of rows. Unlike the symmetric void, this figure has
2n - 1rows and a unique centre row. Keep the counts straight when combining the two patterns in one program. - Negative
repeatcounts.2(n - s)is never negative becauses ≤ n, but if you derivesfrom a different formula, guard against" ".repeat(negative), which throwsIllegalArgumentException. Math.abs.n - ris negative in the lower wing; forgetting the absolute value makessgrow pastnand the gap negative. The dry run above shows the sign flip atr = 4.- Trailing newline. Each row, including the last, ends in
\n; print withSystem.out.print.
Related problems
- Pattern 19 is the complementary figure: solid at the top and bottom, hollow in the middle, with
2nrows. - Pattern 17 uses the same “peak minus distance from the centre” formula horizontally for letters.
- Pattern 22 applies the distance idea in two dimensions.
- Pattern 12 is the numeric cousin with a shrinking central gap.