The zigzag pattern places characters top-to-bottom in a column, then diagonally upward until they reach the top row again, then top-to-bottom again, and so on. This creates a "V" shape that repeats.
For numRows = 4, the placement order looks like this:
The characters go down rows 0 through 3, then back up through rows 2 and 1, then down again. That down-and-up cycle has a length of 2 * numRows - 2. For 4 rows, the cycle length is 6. This cycle length drives every efficient solution below.
When numRows = 1 or numRows >= len(s), the zigzag pattern equals the original string and no rearrangement happens. Every solution handles this case with an early return.
1 <= numRows <= 1000 → numRows can exceed the string length. In that case, every character occupies its own row and the output equals the input, which is why each approach starts with the numRows >= len(s) early return.1 <= s.length <= 1000 → The output is a permutation of the input, so the answer always fits in a fixed-width integer index. No overflow concerns arise even with the index arithmetic in Approach 3.Simulate the zigzag directly. Create a 2D grid, place each character in its cell by moving down and then diagonally up, then read the grid row by row.
This mirrors drawing the pattern on paper. It is straightforward to reason about, but it wastes space because most cells in the grid stay empty.
numRows is 1 or greater than or equal to the string length, return the string as-is.numRows rows and n columns, initialized to empty.Loading animation...
The bottleneck is the 2D grid. We allocate numRows * n cells, but only n of them ever hold a character. The column of each character is irrelevant to the final answer, since we read row by row. The next approach drops the grid and tracks only each character's row, appending it directly to a per-row list.
Instead of a full 2D grid, use one list (or StringBuilder) per row. While iterating through the string, track which row the current character belongs to and append it there. The row bounces between 0 and numRows-1 exactly as in the simulation, but the wasted columns disappear.
The result is the same reading order as Approach 1, with O(n) space instead of O(n * numRows).
numRows is 1 or greater than or equal to the string length, return the string as-is.numRows empty strings (or StringBuilders).currentRow = 0 and goingDown = false.currentRow.currentRow sits on either edge (row 0 or row numRows-1), flip goingDown.currentRow by +1 when goingDown is true, by -1 otherwise.Flipping only at the two edges is what keeps the walk inside the array. goingDown starts false, so the first character, which lands on row 0, flips it to true and the walk starts downward. Every later arrival at row 0 or row numRows-1 flips it again, turning the walk around before currentRow can step out of range. That is why no bounds check is needed when reading rows[currentRow].
Loading animation...
Approach 2 runs in O(n) time, which is optimal since every character must be read at least once. It still uses O(n) extra space for the row lists. For any given row, the indices of the characters that belong to it follow an arithmetic sequence determined by the cycle length. The next approach computes those indices directly and writes straight to the output, removing the per-row lists.
The zigzag pattern repeats every cycleLen = 2 * numRows - 2 characters. Rather than tracking where each cycle starts, let j walk a single row directly: it begins at index row and advances by cycleLen, which lands on exactly that row's downward-leg characters.
jjrow: two characters per cycle, at j (downward leg) and j + cycleLen - 2 * row (upward leg)The upward-leg offset comes from the geometry of the V. The character at j sits row steps below the top on the way down. Its partner on the way back up sits the same row steps below the top, which is cycleLen - 2 * row positions further along the string. Top and bottom rows lie on the fold of the V, so they appear only once per cycle and have no separate upward character. This lets us read characters directly from the original string, with no simulation and no extra lists.
numRows is 1 there is no zigzag to build, so return the string as-is.cycleLen = 2 * numRows - 2.row from 0 to numRows-1:j from row through the string in increments of cycleLen:s[j] (this is the "down" character).row is not the first or last row, compute second = j + cycleLen - 2 * row and append s[second] when it is within bounds (this is the "up" character).Loading animation...
numRows times, and the inner loop processes characters assigned to that row. Together, they cover all n characters.