Pattern 21
Java source ↗ Edit this article ↗
Jump to code:
Problem
Given a positive integer n, print a hollow rectangle of size n × n: stars along the four borders and spaces everywhere inside. The same idea extends to an rows × cols rectangle, and a general version is included.
Constraints: 1 ≤ n ≤ 100 or so. For n = 1 the single cell is a border cell and is printed as a star; for n = 2 there is no interior yet.
Example 1
n = 4
****
* *
* *
****
First and last rows are solid; the two middle rows have stars only at the ends.
Example 2
n = 5
*****
* *
* *
* *
*****
Three hollow rows between two solid ones.
Reading the pattern
Unlike the triangles, every row here has the same length, so the loops are a plain n × n grid: for i in 0..n-1, for j in 0..n-1. The entire pattern lives in the cell rule: a cell (i, j) is a star if it lies on any of the four borders, i.e. if
i == 0 || i == n - 1 || j == 0 || j == n - 1
and a space otherwise. This “decide each cell from its coordinates” style is the one that scales to arbitrary two-dimensional figures: diagonals (i == j), checkerboards ((i + j) % 2 == 0), frames of thickness t (min(i, j, n-1-i, n-1-j) < t), and so on.
A second observation gives a very different program: the rectangle contains only two distinct rows, a solid one (*****) and a hollow one (* *). Build each once and concatenate solid + hollow × (n - 2) + solid. That is the “row template” view, and String.repeat makes it a few lines.
Both methods return the figure as a single String with a newline after every row, so main compares them with expected strings and checks that every n yields exactly 4n - 4 stars (for n ≥ 2) with a blank interior.
Approach 1: Visit every cell and apply the border rule
Intuition. Loop over all n^2 cells. For each one, check whether it touches a border; append '*' if it does and ' ' otherwise. There is no special-casing of the first or last row because the rule covers them automatically through i == 0 and i == n - 1.
Algorithm.
- For
ifrom0ton - 1: - For
jfrom0ton - 1:border = (i == 0 || i == n - 1 || j == 0 || j == n - 1); append'*'ifborder, else' '. - End the row with a newline.
static String nestedLoops(int n) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
boolean border = i == 0 || i == n - 1 || j == 0 || j == n - 1;
sb.append(border ? '*' : ' ');
}
sb.append('\n');
}
return sb.toString();
}
Complexity. Time O(n^2): one constant-time decision per cell. Space O(n^2) for the output string, constant extra memory.
Note border ? '*' : ' ': both branches are char, so the conditional expression has type char and append(char) is chosen. If one branch were a String, the whole expression would become a String and still work; mixing a char with an int would be the trap, since the expression would then be an int and print a number.
Approach 2: Two row templates and repeat
Intuition. Only two kinds of row exist. solid is n stars; hollow is a star, n - 2 spaces, and a star. The figure is solid, then n - 2 copies of hollow, then solid again. Including the newline inside each template makes hollow.repeat(n - 2) produce complete rows.
Two small sizes need care. For n = 1 there is a single row *, not two solid rows, so it is handled first. For n = 2 the template formula works unchanged: hollow would be ** but is repeated zero times, leaving two solid rows.
Algorithm.
- If
n ≤ 0return""; ifn == 1return"*\n". solid = "*" × n + "\n",hollow = "*" + " " × (n - 2) + "*\n".- Return
solid + hollow × (n - 2) + solid.
static String withRepeat(int n) {
if (n <= 0) return "";
if (n == 1) return "*\n";
String solid = "*".repeat(n) + "\n";
String hollow = "*" + " ".repeat(n - 2) + "*\n";
return solid + hollow.repeat(n - 2) + solid;
}
Dry run for n = 4:
| piece | value |
|---|---|
solid |
****\n |
hollow |
* *\n |
hollow.repeat(2) |
* *\n* *\n |
| result | ****\n* *\n* *\n****\n |
Complexity. Time O(n^2): repeat copies n - 2 rows of n + 1 characters. Space O(n^2) for the output plus O(n) for the two templates.
General rectangle
The cell rule does not care whether the grid is square. Replace n by rows for the row tests and by cols for the column tests:
static String rectangle(int rows, int cols) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
boolean border = i == 0 || i == rows - 1 || j == 0 || j == cols - 1;
sb.append(border ? '*' : ' ');
}
sb.append('\n');
}
return sb.toString();
}
rectangle(n, n) must equal the square versions, and the tests check that for every n up to 20, together with a few non-square cases such as rectangle(3, 5), which is *****, * *, *****.
Complexity. Time and output space O(rows × cols).
Edge cases and pitfalls
n = 0. No rows. Approach 1 returns the empty string because its loops do not run; Approach 2 returns it explicitly.n = 1. A single*. Without the special case, Approach 2 would return two solid rows, and" ".repeat(-1)would throw.n = 2. Two solid rows**. There is no interior, and the hollow template is repeated zero times.- Special-casing rows instead of cells. Writing
if (i == 0 || i == n - 1) print solid else print hollowworks but duplicates the row-building logic; the cell rule is shorter and generalises to other figures. - Using
&&instead of||.i == 0 && j == 0marks only one corner. A border cell satisfies any of the four conditions. - Counting stars. For
n ≥ 2the border has4n - 4cells (each corner is shared by two sides). The tests use this closed form as an independent check on the output. - Trailing spaces. The hollow rows end in a star, so no row ends with whitespace, and every row has exactly
ncharacters. This differs from patterns that strip trailing spaces; here the shape itself guarantees it. - Printing. The string already ends with a newline; use
System.out.print.
Related problems
- Pattern 1 is the solid
n × nsquare that this pattern hollows out. - Pattern 22 generalises the border test to “distance from the nearest border”, producing nested rings.
- Pattern 19 and Pattern 20 are other star figures with spaces in the middle, defined row by row instead of cell by cell.
- Pattern 16 also uses
String.repeatto build each row in one call.