Spiral Matrix IV
MedPrompt
You are given two integers m and n, which represent the dimensions of a matrix.
You are also given the head of a linked list of integers.
Generate an m x n matrix that contains the integers in the linked list presented in spiral order (clockwise), starting from the top-left of the matrix. If there are remaining empty spaces, fill them with -1.
Return the generated matrix.
Example 1:
Input: m = 3, n = 5, head = [3,0,2,6,8,1,7,9,4,2,5,5,0]
Output: [[3,0,2,6,8],[5,0,-1,-1,1],[5,2,4,9,7]]
Explanation: The diagram above shows how the values are printed in the matrix.
Note that the remaining spaces in the matrix are filled with -1.Example 2:
Input: m = 1, n = 4, head = [0,1,2]
Output: [[0,1,2,-1]]
Explanation: The diagram above shows how the values are printed from left to right in the matrix.
The last space in the matrix is set to -1.
Constraints:
1 <= m, n <= 1051 <= m * n <= 105- The number of nodes in the list is in the range
[1, m * n]. 0 <= Node.val <= 1000
Approaches
2 approaches with complexity analysis and trade-offs.
This approach simulates the spiral traversal by keeping track of the current position (row, col) and the current direction of movement. It uses a separate visited matrix to avoid revisiting cells and to know when to change direction.
Algorithm
- Create an
m x nresult matrixmatrixand initialize all its elements to-1. - Create an
m x nboolean matrixvisitedand initialize all its elements tofalse. - Initialize the starting position
(row, col)to(0, 0)and the initial directiondirto0(representing 'right'). - Define direction vectors for row and column changes:
dr = {0, 1, 0, -1}anddc = {1, 0, -1, 0}. - Use a pointer
currto traverse the linked list, starting from thehead. - Loop as long as
curris notnull:- Place the value
curr.valatmatrix[row][col]. - Mark the cell as visited:
visited[row][col] = true. - Advance the list pointer:
curr = curr.next. - If the list is exhausted, break the loop.
- Calculate the next potential position
(nextRow, nextCol)using the current direction. - If
(nextRow, nextCol)is out of bounds or has already been visited, change the direction by turning clockwise:dir = (dir + 1) % 4. - Update the current position
(row, col)using the new direction.
- Place the value
- Return the
matrix.
Walkthrough
In this method, we simulate the path of a point moving through the matrix. We begin by creating the m x n result matrix and pre-filling it with -1. This handles the requirement for default values in unused cells. We also need a second m x n boolean matrix, visited, to keep track of which cells have been filled.
The traversal starts at the top-left corner (0, 0). We maintain the current direction of travel (e.g., right, down, left, up). We iterate as long as there are nodes left in the linked list. In each step, we fill the current cell, mark it as visited, and advance the linked list pointer. Then, we check if the next cell in the current direction is valid (i.e., within the matrix boundaries and not yet visited). If it's not valid, we know we've hit a wall or a previously filled path, so we turn clockwise to the next direction. Finally, we update our current coordinates to move to the next cell. This process naturally carves out a spiral path.
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */class Solution { public int[][] spiralMatrix(int m, int n, ListNode head) { int[][] matrix = new int[m][n]; for (int i = 0; i < m; i++) { java.util.Arrays.fill(matrix[i], -1); } boolean[][] visited = new boolean[m][n]; int[] dr = {0, 1, 0, -1}; // Direction vectors for row int[] dc = {1, 0, -1, 0}; // Direction vectors for col int row = 0, col = 0, dir = 0; ListNode curr = head; // We can iterate m * n times, as that's the max number of cells to fill. for (int i = 0; i < m * n; i++) { if (curr == null) { break; } matrix[row][col] = curr.val; visited[row][col] = true; curr = curr.next; int nextRow = row + dr[dir]; int nextCol = col + dc[dir]; if (nextRow < 0 || nextRow >= m || nextCol < 0 || nextCol >= n || visited[nextRow][nextCol]) { dir = (dir + 1) % 4; // Change direction } row += dr[dir]; col += dc[dir]; } return matrix; }}Complexity
Time
O(m * n)
Space
O(m * n)
Trade-offs
Pros
The logic is a direct simulation of a point moving and turning, which can be intuitive to reason about.
Cons
Requires O(m * n) extra space for the
visitedmatrix, making it less space-efficient than the layer-by-layer approach.
This is the most common and efficient approach for spiral matrix problems. It works by traversing the matrix in layers, from the outermost layer to the innermost. Four pointers (top, bottom, left, right) are used to define the boundaries of the current layer being traversed, and these boundaries shrink after each side is completed.
Algorithm
- Create an
m x nresult matrixmatrixand initialize all its elements to-1. - Initialize four boundary pointers:
top = 0,bottom = m - 1,left = 0,right = n - 1. - Use a pointer
currto traverse the linked list, starting from thehead. - Loop as long as
top <= bottomandleft <= right.- Traverse Right: Iterate from
j = lefttoright. In each step, ifcurris not null, setmatrix[top][j] = curr.valand advancecurr. After the row is filled, incrementtop. - Traverse Down: Iterate from
i = toptobottom. Ifcurris not null, setmatrix[i][right] = curr.valand advancecurr. After the column is filled, decrementright. - Traverse Left: Check if
top <= bottom. If so, iterate fromj = righttoleft. Ifcurris not null, setmatrix[bottom][j] = curr.valand advancecurr. After the row is filled, decrementbottom. - Traverse Up: Check if
left <= right. If so, iterate fromi = bottomtotop. Ifcurris not null, setmatrix[i][left] = curr.valand advancecurr. After the column is filled, incrementleft.
- Traverse Right: Iterate from
- If
currbecomesnullat any point, the loops will continue but won't fill any more cells, effectively terminating the process. - Return the
matrix.
Walkthrough
This optimal approach avoids the need for an extra visited matrix by intelligently shrinking the boundaries of the matrix after each full pass of a row or column. We start by initializing the m x n result matrix with -1.
We define four pointers: top, bottom, left, and right, which represent the boundaries of the current rectangular layer we are filling. Initially, they correspond to the full matrix dimensions.
The process is a loop that continues as long as the boundaries have not crossed (i.e., top <= bottom and left <= right) and we still have nodes in our linked list.
Inside the loop, we perform four distinct steps to fill the four sides of the current layer in a clockwise direction:
- Fill the top row from left to right.
- Fill the rightmost column from top to bottom.
- Fill the bottom row from right to left.
- Fill the leftmost column from bottom to top.
After each step, we update the corresponding boundary pointer (e.g., after filling the top row, we increment top) to shrink the area for the next iteration. We must also check if the linked list has been exhausted before filling each cell. The conditions if (top <= bottom) and if (left <= right) are crucial before the third and fourth steps to handle matrices that are not square (e.g., a single row or column).
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */class Solution { public int[][] spiralMatrix(int m, int n, ListNode head) { int[][] matrix = new int[m][n]; for (int i = 0; i < m; i++) { java.util.Arrays.fill(matrix[i], -1); } int top = 0, bottom = m - 1; int left = 0, right = n - 1; ListNode curr = head; while (curr != null && top <= bottom && left <= right) { // Traverse Right for (int j = left; j <= right && curr != null; j++) { matrix[top][j] = curr.val; curr = curr.next; } top++; // Traverse Down for (int i = top; i <= bottom && curr != null; i++) { matrix[i][right] = curr.val; curr = curr.next; } right--; // Traverse Left (check if row still exists) if (top <= bottom) { for (int j = right; j >= left && curr != null; j--) { matrix[bottom][j] = curr.val; curr = curr.next; } bottom--; } // Traverse Up (check if column still exists) if (left <= right) { for (int i = bottom; i >= top && curr != null; i--) { matrix[i][left] = curr.val; curr = curr.next; } left++; } } return matrix; }}Complexity
Time
O(m * n), as each cell of the matrix is visited and filled exactly once.
Space
O(1) auxiliary space. The O(m * n) space for the output matrix is required by the problem and not considered auxiliary.
Trade-offs
Pros
Optimal space complexity, as it only uses a few variables for pointers and does not require an auxiliary
visitedmatrix.Highly efficient time complexity, visiting each cell exactly once.
Cons
The logic involving four separate loops and updating boundaries can be slightly more complex to implement without off-by-one errors.
Solutions
Solution
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode() {} * ListNode(int val) { this.val = val; } * ListNode(int val, ListNode next) { this.val = val; this.next = next; } * } */ class Solution { public int [][] spiralMatrix ( int m , int n , ListNode head ) { int [][] ans = new int [ m ][ n ]; for ( int [] row : ans ) { Arrays . fill ( row , - 1 ); } int i = 0 , j = 0 , p = 0 ; int [][] dirs = { { 0 , 1 }, { 1 , 0 }, { 0 , - 1 }, {- 1 , 0 } }; while ( true ) { ans [ i ][ j ] = head . val ; head = head . next ; if ( head == null ) { break ; } while ( true ) { int x = i + dirs [ p ][ 0 ], y = j + dirs [ p ][ 1 ]; if ( x < 0 || y < 0 || x >= m || y >= n || ans [ x ][ y ] >= 0 ) { p = ( p + 1 ) % 4 ; } else { i = x ; j = y ; break ; } } } return ans ; } }/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: vector < vector < int >> spiralMatrix ( int m , int n , ListNode * head ) { vector < vector < int >> ans ( m , vector < int > ( n , - 1 )); int i = 0 , j = 0 , p = 0 ; vector < vector < int >> dirs = { { 0 , 1 }, { 1 , 0 }, { 0 , - 1 }, { - 1 , 0 } }; while ( 1 ) { ans [ i ][ j ] = head -> val ; head = head -> next ; if ( ! head ) break ; while ( 1 ) { int x = i + dirs [ p ][ 0 ], y = j + dirs [ p ][ 1 ]; if ( x < 0 || y < 0 || x >= m || y >= n || ans [ x ][ y ] >= 0 ) p = ( p + 1 ) % 4 ; else { i = x , j = y ; break ; } } } return ans ; } };# Definition for singly-linked list. # class ListNode: # def __init__(self, val=0, next=None): # self.val = val # self.next = next class Solution : def spiralMatrix ( self , m : int , n : int , head : Optional [ ListNode ]) -> List [ List [ int ]]: ans = [[ - 1 ] * n for _ in range ( m )] i = j = p = 0 dirs = [[ 0 , 1 ], [ 1 , 0 ], [ 0 , - 1 ], [ - 1 , 0 ]] while 1 : ans [ i ][ j ] = head . val head = head . next if not head : break while 1 : x , y = i + dirs [ p ][ 0 ], j + dirs [ p ][ 1 ] if x < 0 or y < 0 or x >= m or y >= n or ~ ans [ x ][ y ]: p = ( p + 1 ) % 4 else : i , j = x , y break return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.