Editorial solutions and visualizations are prepared with care, but we do not guarantee 100% accuracy or completeness. Please use your own judgment to verify results. Visucodize is not responsible for decisions made based on this content.
Solution code
constUPPER_HL='upper_hl',UPPER_HL_COLOR='gold';constSYMM_HL='symm_hl',SYMM_HL_COLOR='blue';constCURRENT_ROW='current_row',CURRENT_ROW_COLOR='gray';functionvisMainFunction(inputMatrix){if(inputMatrix.length ===0|| inputMatrix[0].length ===0){notification(`β <b>Invalid input:</b> The matrix is empty. Returning.`);return;}initClassProperties();explanationAndColorSchemaNotifications();const matrix =newVisArray2D(...(inputMatrix.map(row=>newVisArray(...row)))).options({label:'Matrix'});const n = matrix.length;// Step 1: Transpose the matrixnotification(`
π <b>Step 1: ${makeColoredSpan('Transposing the matrix',UPPER_HL_COLOR)}</b>
We swap <code>matrix[rowIndex][colIndex]</code> β <code>matrix[colIndex][rowIndex]</code>
for all pairs <code>(rowIndex, colIndex)</code> above the main diagonal, effectively converting rows into columns.
Each outer iteration <code>rowIndex</code> completes the transpose of row <code>rowIndex</code> and column <code>rowIndex</code>,
gradually shrinking the active submatrix toward the bottom-right corner.
`);for(let rowIndex =0; rowIndex < n; rowIndex++){notification(`
π <b>Completing transformation of row ${makeColoredSpan(rowIndex,UPPER_HL_COLOR)} and column ${makeColoredSpan(rowIndex,UPPER_HL_COLOR)}</b> β
visiting the <b>top row</b> of the submatrix
<code>matrix[${rowIndex}..n-1][${rowIndex}..n-1]</code>, excluding its top-left diagonal cell
<code>matrix[${rowIndex}][${rowIndex}]</code>. Each visited element is swapped with its
<b>symmetric counterpart</b> <code>matrix[colIndex][${rowIndex}]</code> located below the diagonal,
ensuring both the row and column are fully transposed after this iteration.
`);for(let colIndex = rowIndex +1; colIndex < n; colIndex++){startPartialBatch();// Highlight the pair with distinct classes/colorshighlightSymmetricPair(matrix, rowIndex, colIndex);notification(`π <b>Swap symmetric pair across the main diagonal</b>:
<br/><code>matrix[${makeColoredSpan(rowIndex,UPPER_HL_COLOR)}][${makeColoredSpan(colIndex,UPPER_HL_COLOR)}]</code>
β <code>matrix[${makeColoredSpan(colIndex,SYMM_HL_COLOR)}][${makeColoredSpan(rowIndex,SYMM_HL_COLOR)}]</code><br/>
Before: <b>${makeColoredSpan(matrix[rowIndex][colIndex],UPPER_HL_COLOR)}</b> β <b>${makeColoredSpan(matrix[colIndex][rowIndex],SYMM_HL_COLOR)}</b><br/>
After this swap, each value moves to its correct <b>position</b> in the <b>transposed matrix</b>.`);let temp = matrix[rowIndex][colIndex];
matrix[rowIndex][colIndex]= matrix[colIndex][rowIndex];
matrix[colIndex][rowIndex]= temp;endBatch();}}startPartialBatch();clearHighlights(matrix);notification(`β <b>${makeColoredSpan('Transpose complete',UPPER_HL_COLOR)}!</b> Rows have become columns (values now sit on their target rows for the rotated image).`);endBatch();// Step 2: Reverse each rownotification(`π <b>Step 2: Reverse each row</b> (swap elements left β right).<br/>
After the transpose, each row contains the correct values <b>in reverse order</b>. Reversing each row places them in the correct left-to-right order.`);for(let rowIndex =0; rowIndex < n; rowIndex++){startPartialBatch();notification(`π <b>Reversing row at index ${rowIndex}</b>: swap left β right until the middle.`);highlightRow(matrix, rowIndex);
matrix[rowIndex].reverse();notification(`β Row <b>${rowIndex}</b> reversed successfully.`);endBatch();}notification(`π <b>Rotation complete!</b> The matrix has been successfully rotated 90Β° clockwise.`);return matrix;}functionclearHighlights(matrix){startBatch();for(let r =0; r < matrix.length; r++){for(let c =0; c < matrix[r].length; c++){const vm = matrix.makeVisManagerForIndexPair(r, c);
vm.removeClass(UPPER_HL);
vm.removeClass(SYMM_HL);}}endBatch();}functionhighlightSymmetricPair(matrix, rowIndex, colIndex){startBatch();
matrix.makeVisManagerForIndexPair(rowIndex, colIndex).addClass(UPPER_HL);
matrix.makeVisManagerForIndexPair(colIndex, rowIndex).addClass(SYMM_HL);endBatch();}functionhighlightRow(matrix, row){startBatch();for(let col =0; col < matrix[row].length; col++){
matrix.makeVisManagerForIndexPair(row, col).addClass(CURRENT_ROW);}endBatch();}functionexplanationAndColorSchemaNotifications(){explanationNotification();notification(`
π¨ <b>Color legend</b>
<ul>
<li>
<b>${makeColoredSpan(UPPER_HL_COLOR+' background',UPPER_HL_COLOR)}</b>
(<code>${UPPER_HL}</code>) β highlights the <b>upper-triangle cell</b>
<code>matrix[rowIndex][colIndex]</code> currently being swapped during the transpose step.
</li>
<li>
<b>${makeColoredSpan(SYMM_HL_COLOR+' background',SYMM_HL_COLOR)}</b>
(<code>${SYMM_HL}</code>) β highlights the <b>symmetric counterpart</b>
<code>matrix[colIndex][rowIndex]</code> below the main diagonal that is being swapped with its upper partner.
</li>
<li>
<b>${makeColoredSpan(CURRENT_ROW_COLOR+' background',CURRENT_ROW_COLOR)}</b>
(<code>${CURRENT_ROW}</code>) β highlights the <b>entire row</b> currently being processed in
<b>Step 2</b> (the reversal phase), showing which rowβs elements are being swapped leftβright.
</li>
<li>
When <b>${makeColoredSpan(UPPER_HL_COLOR,UPPER_HL_COLOR)}</b> and
<b>${makeColoredSpan(SYMM_HL_COLOR,SYMM_HL_COLOR)}</b> appear together,
they represent a <b>swap pair</b> across the main diagonal during the transpose.
</li>
</ul>
`);}functioninitClassProperties(){setClassProperties(UPPER_HL,{backgroundColor:UPPER_HL_COLOR});setClassProperties(SYMM_HL,{backgroundColor:SYMM_HL_COLOR});setClassProperties(CURRENT_ROW,{backgroundColor:CURRENT_ROW_COLOR});}
Solution explanation
Solution explanation
Approach
To rotate an n Γ n matrix 90Β° clockwise in place, we perform two sequential in-place transformations:
Step 1 β Transpose the matrix:
Swap matrix[rowIndex][colIndex] β matrix[colIndex][rowIndex]
for all index pairs (rowIndex, colIndex) above the main diagonal, effectively converting rows into columns.
Outer loop (rowIndex):
The loop runs from rowIndex = 0 to n β 1.
At each iteration, we complete the transpose of row rowIndex by moving every element
matrix[rowIndex][colIndex] (where colIndex > rowIndex) to its symmetric position
matrix[colIndex][rowIndex] below the main diagonal.
After this iteration, both the rowIndexth row and rowIndexth column are fully transposed and will not be revisited.
When advancing to the next iteration rowIndex + 1, the algorithm naturally focuses on the smaller submatrix
[rowIndex+1..nβ1] Γ [rowIndex+1..nβ1].
Inner loop (colIndex):
The inner loop starts at colIndex = rowIndex + 1 and runs to n β 1,
visiting only the cells above the diagonal in the top row of the current submatrix
matrix[rowIndex..nβ1][rowIndex..nβ1].
Each visited cell matrix[rowIndex][colIndex] is swapped exactly once with its
symmetric counterpartmatrix[colIndex][rowIndex] below the diagonal.
No double-swapping:
Because the inner loop begins after the diagonal (colIndex = rowIndex + 1),
each off-diagonal pair is swapped exactly once.
Diagonal elements matrix[rowIndex][rowIndex] are fixed points and remain unchanged.
Step 2 β Reverse each row:
After the transpose, each row contains the correct values but in reverse order.
Reversing each row restores the proper left-to-right order, completing the
90Β° clockwise rotation.
This two-step algorithm runs fully in place and linearly with respect to the number of elements:
each off-diagonal pair is swapped once during transpose, and every row is reversed once to finalize the rotation.
Time Complexity
O(nΒ²) β each element is visited once during the transpose and once during the row reversal.
Space Complexity
O(1) β the rotation is performed in place
Input-1
Visucodize editorial solution titled JS Solution for LeetCode's Rotate Image coding problem. Includes code and may include explanation. You can animate the code step by step using the provided input by clicking the run button, or fork it locally to update the code and input for custom visualization. View the original problem at https://leetcode.com/problems/rotate-image.