LeetCode #6: Zigzag Conversion — Solved in Java
Row-by-Row with a List and Cycle Math With One Builder
This one asks you to take a string and write it out in a zigzag pattern across a certain number of rows. Then, read it row by row to form the final output. You don’t have to draw anything. Just think of each row as a separate line of text you’re filling in as you go. There’s a simple version that builds each row one at a time, and a more direct one that uses the repeating zigzag pattern to figure out where each character belongs without tracking direction or using multiple structures. You’ll see both written out in Java below, with the full explanation after to each.
You’re given a string
s
and a number callednumRows
. You need to rearrange the letters in a zigzag pattern and then return the result by reading each row in order.Example 1
Input:
s = "PAYPALISHIRING"
,numRows = 3
Output:"PAHNAPLSIIGYIR"
How it would look:
Example 2
Input:
s = "PAYPALISHIRING"
,numRows = 4
Output:"PINALSIGYAHRPI"
How it would look:
Constraints
1 <=
s.length
<= 1000s consists of English letters (lower-case and upper-case), ',' and '.'.
1 <=
numRows
<= 1000
Solution 1 — Row-by-Row with a List
This version builds each row as you walk through the string. It moves through the characters, keeping track of which row you’re on and whether you're going down or up. When you reach the top or bottom, you change direction.
Let’s go through it:
class Solution {
public String convert(String s, int numRows) {
This defines the class and the method. The method takes in a string and the number of rows for the zigzag.
if (numRows == 1 || s.length() <= numRows) return s;
This checks if a zigzag is even needed. If the number of rows is one, or the string is too short to wrap, we just return it as is.
List<StringBuilder> rows = new ArrayList<>();
for (int i = 0; i < Math.min(numRows, s.length()); i++) {
rows.add(new StringBuilder());
}
We create one StringBuilder
for each row. We don’t need more than the length of the string, so we take the smaller of numRows
and s.length()
.
int currRow = 0;
boolean goingDown = false;
currRow
keeps track of which row we’re on. The goingDown
flag tells us which direction we’re moving.
for (char c : s.toCharArray()) {
rows.get(currRow).append(c);
We loop through each character in the string and append it to the current row.
if (currRow == 0 || currRow == numRows - 1) goingDown = !goingDown;
If we’re at the top or bottom row, we flip direction.
currRow += goingDown ? 1 : -1;
}
We move up or down to the next row depending on which way we’re going.
StringBuilder result = new StringBuilder();
for (StringBuilder row : rows) result.append(row);
return result.toString();
}
}
After we’ve filled out the rows, we combine them into one final string and return it.
This loops through the string once, so it runs in O(n) time. IIt uses O(n + numRows) space. That’s O(n) for the characters and O(numRows) for the row builders. It’s easy to follow, works well across inputs, and doesn’t rely on any math tricks or index juggling. That makes it great for walking through with someone step by step.
Solution 2 — Cycle Math With One Builder
This solution skips the direction tracking and builds the result by figuring out character positions using math. Every full zigzag cycle takes 2 * numRows - 2
steps. That pattern repeats throughout the string.
Let’s walk through it:
class Solution {
public String convert(String s, int numRows) {
We start by defining the class and the method. It takes a string and a row count.
if (numRows == 1 || s.length() <= numRows) return s;
If the zigzag wouldn’t change the string, we return it right away.
int n = s.length();
StringBuilder res = new StringBuilder(n);
We get the string length and set up a builder to hold the final result.
int cycle = 2 * numRows - 2;
This value is the number of characters in one full zigzag cycle.
for (int row = 0; row < numRows; row++) {
We loop through each row, one at a time.
for (int j = row; j < n; j += cycle) {
This loop picks every vertical character for the current row. They show up every cycle
characters apart.
res.append(s.charAt(j));
We add the vertical character directly.
int diag = j + cycle - 2 * row;
This calculates the diagonal character that appears in the middle of the zigzag, for rows that aren’t the top or bottom.
if (row != 0 && row != numRows - 1 && diag < n) {
res.append(s.charAt(diag));
}
}
}
If it’s a middle row and the diagonal index is still in bounds, we include it too.
return res.toString();
}
}
After all rows are processed, we return the completed string.
This also runs in O(n) time, and it uses O(n) space total. Only the final string is built, with no row tracking. It cuts down on memory overhead by skipping lists and extra flags. Everything goes into one builder. It’s compact, avoids backtracking, and works well in interviews where you want to show control over how data is placed.
Row-by-Row — Copy and Paste
class Solution {
public String convert(String s, int numRows) {
if (numRows == 1 || s.length() <= numRows) return s;
List<StringBuilder> rows = new ArrayList<>();
for (int i = 0; i < Math.min(numRows, s.length()); i++) {
rows.add(new StringBuilder());
}
int currRow = 0;
boolean goingDown = false;
for (char c : s.toCharArray()) {
rows.get(currRow).append(c);
if (currRow == 0 || currRow == numRows - 1) goingDown = !goingDown;
currRow += goingDown ? 1 : -1;
}
StringBuilder result = new StringBuilder();
for (StringBuilder row : rows) result.append(row);
return result.toString();
}
}
Cycle Math — Copy and Paste
class Solution {
public String convert(String s, int numRows) {
if (numRows == 1 || s.length() <= numRows) return s;
int n = s.length();
StringBuilder res = new StringBuilder(n);
int cycle = 2 * numRows - 2;
for (int row = 0; row < numRows; row++) {
for (int j = row; j < n; j += cycle) {
res.append(s.charAt(j));
int diag = j + cycle - 2 * row;
if (row != 0 && row != numRows - 1 && diag < n) {
res.append(s.charAt(diag));
}
}
}
return res.toString();
}
}