Please upgrade to NeetCode Pro to view company tags.
Prerequisites
Before attempting this problem, you should be comfortable with:
2D Array Manipulation - Creating and filling matrices with values
Boundary Tracking - Maintaining and updating top, bottom, left, right boundaries during traversal
Direction Vectors - Using coordinate deltas (dr, dc) to control movement direction
Simulation - Following a defined pattern (spiral) to systematically fill cells
1. Iteration
Intuition
We fill an n x n matrix with values from 1 to n^2 in spiral order. Think of peeling an onion layer by layer. We maintain four boundaries (top, bottom, left, right) and fill each layer by moving right along the top row, down the right column, left along the bottom row, and up the left column. After completing each direction, we shrink the corresponding boundary inward.
Algorithm
Initialize an n x n matrix with zeros and set boundaries: left = 0, right = n - 1, top = 0, bottom = n - 1.
Start with val = 1 and continue while left <= right:
Fill the top row from left to right, then increment top.
Fill the right column from top to bottom, then decrement right.
Fill the bottom row from right to left, then decrement bottom.
Fill the left column from bottom to top, then increment left.
classSolution:defgenerateMatrix(self, n:int)-> List[List[int]]:
mat =[[0]* n for _ inrange(n)]
left, right =0, n -1
top, bottom =0, n -1
val =1while left <= right:# Fill every val in top rowfor c inrange(left, right +1):
mat[top][c]= val
val +=1
top +=1# Fill every val in right colfor r inrange(top, bottom +1):
mat[r][right]= val
val +=1
right -=1# Fill every val in bottom row (reverse order)for c inrange(right, left -1,-1):
mat[bottom][c]= val
val +=1
bottom -=1# Fill every val in the left col (reverse order)for r inrange(bottom, top -1,-1):
mat[r][left]= val
val +=1
left +=1return mat
publicclassSolution{publicint[][]generateMatrix(int n){int[][] mat =newint[n][n];int left =0, right = n -1, top =0, bottom = n -1, val =1;while(left <= right){// Fill every val in top rowfor(int c = left; c <= right; c++){
mat[top][c]= val++;}
top++;// Fill every val in right colfor(int r = top; r <= bottom; r++){
mat[r][right]= val++;}
right--;// Fill every val in bottom row (reverse order)for(int c = right; c >= left; c--){
mat[bottom][c]= val++;}
bottom--;// Fill every val in the left col (reverse order)for(int r = bottom; r >= top; r--){
mat[r][left]= val++;}
left++;}return mat;}}
classSolution{public:
vector<vector<int>>generateMatrix(int n){
vector<vector<int>>mat(n,vector<int>(n,0));int left =0, right = n -1, top =0, bottom = n -1, val =1;while(left <= right){// Fill every val in top rowfor(int c = left; c <= right; c++){
mat[top][c]= val++;}
top++;// Fill every val in right colfor(int r = top; r <= bottom; r++){
mat[r][right]= val++;}
right--;// Fill every val in bottom row (reverse order)for(int c = right; c >= left; c--){
mat[bottom][c]= val++;}
bottom--;// Fill every val in the left col (reverse order)for(int r = bottom; r >= top; r--){
mat[r][left]= val++;}
left++;}return mat;}};
classSolution{/**
* @param {number} n
* @return {number[][]}
*/generateMatrix(n){const mat = Array.from({length: n },()=>Array(n).fill(0));let left =0,
right = n -1,
top =0,
bottom = n -1,
val =1;while(left <= right){// Fill every val in top rowfor(let c = left; c <= right; c++){
mat[top][c]= val++;}
top++;// Fill every val in right colfor(let r = top; r <= bottom; r++){
mat[r][right]= val++;}
right--;// Fill every val in bottom row (reverse order)for(let c = right; c >= left; c--){
mat[bottom][c]= val++;}
bottom--;// Fill every val in the left col (reverse order)for(let r = bottom; r >= top; r--){
mat[r][left]= val++;}
left++;}return mat;}}
publicclassSolution{publicint[][]GenerateMatrix(int n){int[][] mat =newint[n][];for(int i =0; i < n; i++) mat[i]=newint[n];int left =0, right = n -1;int top =0, bottom = n -1;int val =1;while(left <= right){for(int c = left; c <= right; c++){
mat[top][c]= val;
val++;}
top++;for(int r = top; r <= bottom; r++){
mat[r][right]= val;
val++;}
right--;for(int c = right; c >= left; c--){
mat[bottom][c]= val;
val++;}
bottom--;for(int r = bottom; r >= top; r--){
mat[r][left]= val;
val++;}
left++;}return mat;}}
funcgenerateMatrix(n int)[][]int{
mat :=make([][]int, n)for i :=range mat {
mat[i]=make([]int, n)}
left, right :=0, n-1
top, bottom :=0, n-1
val :=1for left <= right {for c := left; c <= right; c++{
mat[top][c]= val
val++}
top++for r := top; r <= bottom; r++{
mat[r][right]= val
val++}
right--for c := right; c >= left; c--{
mat[bottom][c]= val
val++}
bottom--for r := bottom; r >= top; r--{
mat[r][left]= val
val++}
left++}return mat
}
class Solution {fungenerateMatrix(n: Int): Array<IntArray>{val mat =Array(n){IntArray(n)}var left =0var right = n -1var top =0var bottom = n -1var value =1while(left <= right){for(c in left..right){
mat[top][c]= value++}
top++for(r in top..bottom){
mat[r][right]= value++}
right--for(c in right downTo left){
mat[bottom][c]= value++}
bottom--for(r in bottom downTo top){
mat[r][left]= value++}
left++}return mat
}}
classSolution{funcgenerateMatrix(_ n:Int)->[[Int]]{var mat =Array(repeating:Array(repeating:0, count: n), count: n)varleft=0,right= n -1var top =0, bottom = n -1var val =1whileleft<=right{for c inleft...right{
mat[top][c]= val
val +=1}
top +=1for r in top...bottom {
mat[r][right]= val
val +=1}right-=1for c instride(from:right, through:left, by:-1){
mat[bottom][c]= val
val +=1}
bottom -=1for r instride(from: bottom, through: top, by:-1){
mat[r][left]= val
val +=1}left+=1}return mat
}}
Time & Space Complexity
Time complexity: O(n2)
Space complexity: O(n2) for the output matrix.
2. Recursion
Intuition
The recursive approach mirrors the iterative one but uses function calls to handle each layer. We fill one complete ring of the spiral (top row, right column, bottom row, left column) and then recursively fill the inner portion. The base case is when the boundaries cross, meaning the entire matrix is filled.
Algorithm
Create a helper function fill(left, right, top, bottom, val) that fills one layer.
Base case: if left > right or top > bottom, return.
Fill the current layer in four directions (same as iterative approach), updating val after each cell.
After filling the outer ring, recursively call fill with updated boundaries for the inner layer.
Start the recursion with fill(0, n - 1, 0, n - 1, 1).
classSolution:defgenerateMatrix(self, n:int)-> List[List[int]]:
mat =[[0]* n for _ inrange(n)]deffill(left, right, top, bottom, val):if left > right or top > bottom:return# Fill every val in top rowfor c inrange(left, right +1):
mat[top][c]= val
val +=1
top +=1# Fill every val in right colfor r inrange(top, bottom +1):
mat[r][right]= val
val +=1
right -=1# Fill every val in bottom row (reverse order)for c inrange(right, left -1,-1):
mat[bottom][c]= val
val +=1
bottom -=1# Fill every val in the left col (reverse order)for r inrange(bottom, top -1,-1):
mat[r][left]= val
val +=1
left +=1# Recur for the inner layer
fill(left, right, top, bottom, val)
fill(0, n -1,0, n -1,1)return mat
publicclassSolution{publicint[][]generateMatrix(int n){int[][] mat =newint[n][n];fill(mat,0, n -1,0, n -1,1);return mat;}privatevoidfill(int[][] mat,int left,int right,int top,int bottom,int val){if(left > right || top > bottom)return;// Fill every val in top rowfor(int c = left; c <= right; c++){
mat[top][c]= val++;}
top++;// Fill every val in right colfor(int r = top; r <= bottom; r++){
mat[r][right]= val++;}
right--;// Fill every val in bottom row (reverse order)for(int c = right; c >= left; c--){
mat[bottom][c]= val++;}
bottom--;// Fill every val in the left col (reverse order)for(int r = bottom; r >= top; r--){
mat[r][left]= val++;}
left++;// Recur for the inner layerfill(mat, left, right, top, bottom, val);}}
classSolution{public:
vector<vector<int>>generateMatrix(int n){
vector<vector<int>>mat(n,vector<int>(n,0));fill(mat,0, n -1,0, n -1,1);return mat;}private:voidfill(vector<vector<int>>&mat,int left,int right,int top,int bottom,int val){if(left > right || top > bottom)return;// Fill every val in top rowfor(int c = left; c <= right; c++){
mat[top][c]= val++;}
top++;// Fill every val in right colfor(int r = top; r <= bottom; r++){
mat[r][right]= val++;}
right--;// Fill every val in bottom row (reverse order)for(int c = right; c >= left; c--){
mat[bottom][c]= val++;}
bottom--;// Fill every val in the left col (reverse order)for(int r = bottom; r >= top; r--){
mat[r][left]= val++;}
left++;// Recur for the inner layerfill(mat, left, right, top, bottom, val);}};
classSolution{/**
* @param {number} n
* @return {number[][]}
*/generateMatrix(n){const mat = Array.from({length: n },()=>Array(n).fill(0));constfill=(left, right, top, bottom, val)=>{if(left > right || top > bottom)return;// Fill every val in top rowfor(let c = left; c <= right; c++){
mat[top][c]= val++;}
top++;// Fill every val in right colfor(let r = top; r <= bottom; r++){
mat[r][right]= val++;}
right--;// Fill every val in bottom row (reverse order)for(let c = right; c >= left; c--){
mat[bottom][c]= val++;}
bottom--;// Fill every val in the left col (reverse order)for(let r = bottom; r >= top; r--){
mat[r][left]= val++;}
left++;// Recur for the inner layerfill(left, right, top, bottom, val);};fill(0, n -1,0, n -1,1);return mat;}}
publicclassSolution{publicint[][]GenerateMatrix(int n){int[][] mat =newint[n][];for(int i =0; i < n; i++) mat[i]=newint[n];voidFill(int left,int right,int top,int bottom,int val){if(left > right || top > bottom)return;for(int c = left; c <= right; c++){
mat[top][c]= val;
val++;}
top++;for(int r = top; r <= bottom; r++){
mat[r][right]= val;
val++;}
right--;for(int c = right; c >= left; c--){
mat[bottom][c]= val;
val++;}
bottom--;for(int r = bottom; r >= top; r--){
mat[r][left]= val;
val++;}
left++;Fill(left, right, top, bottom, val);}Fill(0, n -1,0, n -1,1);return mat;}}
funcgenerateMatrix(n int)[][]int{
mat :=make([][]int, n)for i :=range mat {
mat[i]=make([]int, n)}var fill func(left, right, top, bottom, val int)
fill =func(left, right, top, bottom, val int){if left > right || top > bottom {return}for c := left; c <= right; c++{
mat[top][c]= val
val++}
top++for r := top; r <= bottom; r++{
mat[r][right]= val
val++}
right--for c := right; c >= left; c--{
mat[bottom][c]= val
val++}
bottom--for r := bottom; r >= top; r--{
mat[r][left]= val
val++}
left++fill(left, right, top, bottom, val)}fill(0, n-1,0, n-1,1)return mat
}
class Solution {fungenerateMatrix(n: Int): Array<IntArray>{val mat =Array(n){IntArray(n)}funfill(left: Int, right: Int, top: Int, bottom: Int, value: Int){if(left > right || top > bottom)returnvar l = left
var r = right
var t = top
var b = bottom
var v = value
for(c in l..r){
mat[t][c]= v++}
t++for(row in t..b){
mat[row][r]= v++}
r--for(c in r downTo l){
mat[b][c]= v++}
b--for(row in b downTo t){
mat[row][l]= v++}
l++fill(l, r, t, b, v)}fill(0, n -1,0, n -1,1)return mat
}}
classSolution{funcgenerateMatrix(_ n:Int)->[[Int]]{var mat =Array(repeating:Array(repeating:0, count: n), count: n)funcfill(_left:Int,_right:Int,_ top:Int,_ bottom:Int,_ val:Int){ifleft>right|| top > bottom {return}var l =left, r =right, t = top, b = bottom, v = val
for c in l...r {
mat[t][c]= v
v +=1}
t +=1for row in t...b {
mat[row][r]= v
v +=1}
r -=1for c instride(from: r, through: l, by:-1){
mat[b][c]= v
v +=1}
b -=1for row instride(from: b, through: t, by:-1){
mat[row][l]= v
v +=1}
l +=1fill(l, r, t, b, v)}fill(0, n -1,0, n -1,1)return mat
}}
Time & Space Complexity
Time complexity: O(n2)
Space complexity:
O(n) space for recursion stack.
O(n2) space for the output matrix.
3. Iteration (Optimal)
Intuition
Instead of tracking four boundaries, we can use direction vectors to navigate the spiral. We start moving right and change direction (right -> down -> left -> up -> right...) whenever we hit a boundary or an already-filled cell. The direction change follows the pattern of rotating 90 degrees clockwise, which can be computed mathematically: if current direction is (dr, dc), the next direction is (dc, -dr).
Algorithm
Initialize an n x n matrix with zeros. Set starting position (r, c) = (0, 0) and direction (dr, dc) = (0, 1) (moving right).
For each value from 1 to n^2:
Place the value at position (r, c).
Check if the next position (with wraparound) is already filled.
If so, rotate direction by setting (dr, dc) = (dc, -dr).
classSolution:defgenerateMatrix(self, n:int)-> List[List[int]]:
mat =[[0]* n for _ inrange(n)]
r = c =0
dr, dc =0,1for val inrange(n * n):
mat[r][c]= val +1if mat[(r + dr)% n][(c + dc)% n]!=0:
dr, dc = dc,-dr
r, c = r + dr, c + dc
return mat
publicclassSolution{publicint[][]generateMatrix(int n){int[][] mat =newint[n][n];int r =0, c =0, dr =0, dc =1;for(int val =0; val < n * n; val++){
mat[r][c]= val +1;int nextR =(r + dr)% n, nextC =(c + dc)% n;if(nextR <0) nextR += n;if(nextC <0) nextC += n;if(mat[nextR][nextC]!=0){int temp = dr;
dr = dc;
dc =-temp;}
r += dr;
c += dc;}return mat;}}
classSolution{public:
vector<vector<int>>generateMatrix(int n){
vector<vector<int>>mat(n,vector<int>(n,0));int r =0, c =0, dr =0, dc =1;for(int val =0; val < n * n; val++){
mat[r][c]= val +1;int nextR =(r + dr)% n, nextC =(c + dc)% n;if(nextR <0) nextR += n;if(nextC <0) nextC += n;if(mat[nextR][nextC]!=0){int temp = dr;
dr = dc;
dc =-temp;}
r += dr;
c += dc;}return mat;}};
classSolution{/**
* @param {number} n
* @return {number[][]}
*/generateMatrix(n){const mat = Array.from({length: n },()=>Array(n).fill(0));let r =0,
c =0,
dr =0,
dc =1;for(let val =0; val < n * n; val++){
mat[r][c]= val +1;let nextR =(r + dr)% n,
nextC =(c + dc)% n;if(nextR <0) nextR += n;if(nextC <0) nextC += n;if(mat[nextR][nextC]!==0){[dr, dc]=[dc,-dr];}
r += dr;
c += dc;}return mat;}}
publicclassSolution{publicint[][]GenerateMatrix(int n){int[][] mat =newint[n][];for(int i =0; i < n; i++) mat[i]=newint[n];int r =0, c =0;int dr =0, dc =1;for(int val =0; val < n * n; val++){
mat[r][c]= val +1;if(mat[(r + dr + n)% n][(c + dc + n)% n]!=0){int temp = dr;
dr = dc;
dc =-temp;}
r += dr;
c += dc;}return mat;}}
funcgenerateMatrix(n int)[][]int{
mat :=make([][]int, n)for i :=range mat {
mat[i]=make([]int, n)}
r, c :=0,0
dr, dc :=0,1for val :=0; val < n*n; val++{
mat[r][c]= val +1
nextR :=(r + dr + n)% n
nextC :=(c + dc + n)% n
if mat[nextR][nextC]!=0{
dr, dc = dc,-dr
}
r += dr
c += dc
}return mat
}
class Solution {fungenerateMatrix(n: Int): Array<IntArray>{val mat =Array(n){IntArray(n)}var r =0var c =0var dr =0var dc =1for(value in0 until n * n){
mat[r][c]= value +1val nextR =(r + dr + n)% n
val nextC =(c + dc + n)% n
if(mat[nextR][nextC]!=0){val temp = dr
dr = dc
dc =-temp
}
r += dr
c += dc
}return mat
}}
classSolution{funcgenerateMatrix(_ n:Int)->[[Int]]{var mat =Array(repeating:Array(repeating:0, count: n), count: n)var r =0, c =0var dr =0, dc =1for val in0..<(n * n){
mat[r][c]= val +1let nextR =(r + dr + n)% n
let nextC =(c + dc + n)% n
if mat[nextR][nextC]!=0{let temp = dr
dr = dc
dc =-temp
}
r += dr
c += dc
}return mat
}}
Time & Space Complexity
Time complexity: O(n2)
Space complexity: O(n2) for the output matrix.
Common Pitfalls
Incorrect Boundary Updates
Failing to update boundaries (top, bottom, left, right) after filling each edge leads to overwriting previously filled cells or skipping cells entirely. Each boundary must be adjusted immediately after its corresponding edge is filled.
Off-by-One in Range Calculations
When iterating along rows or columns, using inclusive vs exclusive bounds incorrectly causes cells to be missed or written twice. For example, after filling the top row from left to right, the next column fill should start from top + 1, not top.
Forgetting to Handle Odd-Sized Matrices
For odd values of n, the center cell requires special attention. If the loop condition or boundary updates are off, the center cell may be skipped or the loop may not terminate correctly. The direction-based approach handles this naturally, but boundary-based approaches need careful loop conditions.