
LeetCode: Matrix
Matrix intro
LeetCode problems involving matrixes.
What is a Matrix
Its a grid!
Its got a top, right, bottom, left! What more do you want!??!?! Its a matrix!
867. Transpose Matrix ::1:: - Easy
Topics: Array, Matrix, Simulation
Intro
Given a 2D integer array matrix, return the transpose of matrix. The transpose of a matrix is the matrix flipped over its main diagonal, switching the matrix's row and column indices.
| Example Input | Output |
|---|---|
| matrix = [[1,2,3],[4,5,6],[7,8,9]] | [[1,4,7],[2,5,8],[3,6,9]] |
| matrix = [[1,2,3],[4,5,6]] | [[1,4],[2,5],[3,6]] |
Constraints:
m == matrix.length
n == matrix[i].length
1 ≤ m, n ≤ 1000
1 ≤ m * n ≤ 10^5
-10^9 ≤ matrix[i][j] ≤ 10^9
Abstraction
Given an 2D matrix of size m * n, create a new matrix of size n * m, and take the cells from the original matrix (r, c) and flip them to place them into the new matrix (c, r)
Pseudocode
oh! pseudocode hasn't been written yet, try another card! :)Solution 1: Turn N * M Into M * N Matrix And Flip Cell Coords - Math and Geometry/Math and Geometry
def transpose(self, matrix: List[List[int]]) -> List[List[int]]:
# Flip matrix over its main diagonal: matrix[i][j] -> result[j][i]
# Non-square matrix:
# Matrix is not necessarily square, so we transpose n x m matrix => m x n
# The original matrix may not fit the new transformation shape,
# so we must allocate a new matrix to hold the result
# Transpose Ex:
# [1, 2, 3] [1, 4]
# [4, 5, 6] ==> [2, 5]
# [3, 6]
# Note:
# 1. Allocate a new result matrix with cols rows, each rows long
# (dimensions swapped: input is rows x cols -> result is cols x rows)
# 2. For every cell (r, c) in the input, write it to (c, r) in result
# Boundaries
rows, cols = len(matrix), len(matrix[0])
# New Matrix Shape:
# Original: rows x cols -> result: cols x rows (dimensions swapped)
res = []
for _ in range(cols):
new_row = [0] * rows
res.append(new_row)
# Mirror each cell across its diagonal,
# flip (r, c) -> (c, r)
# tc: O(rows*cols)
for r in range(rows):
for c in range(cols):
res[c][r] = matrix[r][c]
# overall: tc O(rows*cols)
# overall: sc O(cols*rows)
return res566. Reshape the Matrix ::1:: - Easy
Topics: Array, Matrix, Simulation
Intro
In MATLAB, there is a handy function called reshape which can reshape an m x n matrix into a new one with a different size r x c keeping its original data. You are given an m x n matrix mat and two integers r and c representing the number of rows and the number of columns of the wanted reshaped matrix. The reshaped matrix should be filled with all the elements of the original matrix in the same row-traversing order as they were. If the reshape operation with given parameters is possible and legal, output the new reshaped matrix; Otherwise, output the original matrix.
| Example Input | Output |
|---|---|
| mat = [[1,2],[3,4]], r = 1, c = 4 | [[1,2,3,4]] |
| mat = [[1,2],[3,4]], r = 2, c = 4 | [[1,2],[3,4]] |
Constraints:
m == mat.length
n == mat[i].length
1 ≤ m, n ≤ 100
-1000 ≤ mat[i][j] ≤ 1000
1 ≤ r, c, ≤ 300
Abstraction
Given a matrix of m * n, transform the original matrix into a single flat sequence by reading top to bottom and left to right, and create a new matrix of size r x c. If there are enough elements, fill the next matrix. If not, return the original matrix unchanged.
Pseudocode
oh! pseudocode hasn't been written yet, try another card! :)Solution 1: Flatten Index Mapping - Math and Geometry/Math and Geometry
def matrixReshape(self, mat: List[List[int]], r: int, c: int) -> List[List[int]]:
# Reshape m x n matrix into r x c, if possible,
# while preserving row reading order
# MATLAB Reshape:
# The reshape operation is a simple reshaping of a matrix.
# All its doing is reading the original matrix in top to bottom (row 0 -> n),
# then writing those same values back into a new matrix of r x c,
# in the same order:
# Reshape Ex (mat 2x2 -> 1x4): Possible
# [1, 2]
# [3, 4] ==> [1, 2, 3, 4]
# Reshape Ex (mat 2x2 -> 2x4): Not Possible
# [1, 2] 2x2 = 4 elements,
# [3, 4] ==> [ , , , ] 2x4 needs 8 -- count mismatch
# [ , , , ]
# Note:
# 1. If total element counts don't match, reshape is impossible
# 2. Otherwise, for each flat index k:
# source coords = (k // nc, k % nc) -- original width nc
# destination coords = (k // c, k % c) -- new width c
# New r x c matrix with same elements, same reading order
# Boundaries
nr, nc = len(mat), len(mat[0])
# Element Count Requirement:
# only continue if total element count matches
if nr * nc != r * c:
return mat
# New Matrix Shape:
# Target shape (r, c) is given directly by the caller -- NOT derived
# from nr/nc (unlike transpose, this isn't a dimension swap)
res = []
for _ in range(r):
new_row = [0] * c
res.append(new_row)
# Flatten and refill via index mapping
# k walks every element in row-major order for both shapes at once
# tc: O(nr*nc)
for k in range(nr * nc):
# Map flat index k to source and destination coordinates
srcRow, srcCol = k // nc, k % nc
dstRow, dstCol = k // c, k % c
res[dstRow][dstCol] = mat[srcRow][srcCol]
# overall: tc O(nr*nc)
# overall: sc O(r*c)
return res1380. Lucky Numbers in a Matrix ::2:: - Easy
Topics: Array, Matrix
Intro
Given an m x n matrix of distinct numbers, return all lucky numbers in the matrix in any order. A lucky number is an element of the matrix such that it is the minimum element in its row and maximum in its column.
| Example Input | Output |
|---|---|
| matrix = [[3,7,8],[9,11,13],[15,16,17]] | [15] |
| matrix = matrix = [[1,10,4,2],[9,3,8,7],[15,16,17,12]] | [12] |
| matrix = [[7,8],[1,2]] | [7] |
Constraints:
m == mat.length
n == mat[i].length
1 ≤ m, n ≤ 50
1 ≤ mat[i][j] ≤ 10^5
All elements in the matrix are distinct.
Abstraction
Given a matrix of m * n, find the lucky number of the matrix, which is the min number in its row and max in its column.
Pseudocode
oh! pseudocode hasn't been written yet, try another card! :)Solution 1: Row Minimums vs Column Maximums - Math and Geometry/Math and Geometry
def luckyNumbers(self, matrix: List[List[int]]) -> List[int]:
# Find elements that are BOTH the min of their row AND max of their column
# Note:
# 1. rowMins[i] = min in row i
# 2. colMaxes[j] = max in column j
# 3. A cell is lucky iff it equals BOTH its row's min AND its column's max
# List of lucky numbers (Only one exists per matrix)
# Lucky Ex:
# [3, 7, 8 ]
# [9, 11, 13] ==> [15] (15 is min of its row, max of its col)
# [15, 16, 17]
m, n = len(matrix), len(matrix[0])
# Min per row:
# tc: O(m*n)
rowMins = []
for row in matrix:
rowMins.append(min(row))
# Max per column:
# tc: O(m*n)
colMaxes = []
for j in range(n):
colVal = []
for i in range(m):
colVal.append(matrix[i][j])
colMaxes.append(max(colVal))
# Check Every Cell For Lucky:
# Needs to match its row's min AND its column's max
# tc: O(m*n)
for i in range(m):
for j in range(n):
if matrix[i][j] == rowMins[i] and matrix[i][j] == colMaxes[j]:
return [matrix[i][j]]
# overall: tc O(m*n)
# overall: sc O(m+n)
return []Solution 2: Pythonic Transpose via zip + Set Intersection - Math and Geometry/Math and Geometry
def luckyNumbers(self, matrix: List[List[int]]) -> List[int]:
# Find elements that are BOTH the min of their row AND max of their column
# Note:
# Since all values are DISTINCT, a value can only ever occupy ONE
# cell in the entire matrix. So instead of checking row/col position per-cell (Solution 1),
# we can just find which values appear in
# BOTH "the set of all row minimums" and "the set of all column maximums"
# set intersection handles the matching for free.
# 1. minrow = set of every row's minimum value
# 2. maxcol = set of every column's maximum value
# (zip(*matrix) transposes the matrix: unpacking matrix's rows
# as separate args to zip() groups the i-th element of every
# row together, which is exactly column i -- no manual index
# math needed)
# 3. minrow & maxcol = values satisfying BOTH conditions at once
# Result -> list of lucky numbers (at most one, given distinct values)
# Lucky Ex:
# [3, 7, 8 ]
# [9, 11, 13] ==> [15] (15 is min of its row, max of its col)
# [15, 16, 17]
# Row minimums, as a set
# tc: O(m*n)
minrow = {min(row) for row in matrix}
# Column maximums, via transpose + set
# zip(*matrix) regroups columns as rows, so max(col) reads
# naturally without manual matrix[i][j] indexing
# tc: O(m*n)
maxcol = {max(col) for col in zip(*matrix)}
# Intersect: values satisfying both conditions
# tc: O(min(m, n)) -- set intersection scales with the smaller set
res = list(minrow & maxcol)
# overall: tc O(m*n)
# overall: sc O(m+n) for minrow + maxcol
return res1572. Matrix Diagonal Sum ::1:: - Easy
Topics: Array, Matrix
Intro
Given a square matrix mat, return the sum of the matrix diagonals. Only include the sum of all the elements on the primary diagonal and all the elements on the secondary diagonal that are not part of the primary diagonal.
| Example Input | Output |
|---|---|
| mat = [[1,2,3],[4,5,6],[7,8,9]] | 25 |
| mat = [[1,1,1,1],[1,1,1,1],[1,1,1,1],[1,1,1,1]] | 8 |
| mat = [[5]] | 5 |
Constraints:
n == mat.length == mat[i].length
1 ≤ n ≤ 100
1 ≤ m, n ≤ 100
1 ≤ mat[i][j] ≤ 100
Abstraction
Given a matrix of m * n, get the total sum of both diagonals, that being primary (top-left to bottom-right) and secondary (top-right to bottom-left) diagonal, without double counting the center cell if its an odd layered matrix.
Pseudocode
oh! pseudocode hasn't been written yet, try another card! :)Solution 1: Primary Top Left To Bottom Right And Secondary Top Right To Bottom Left Diagonals With Center Cell Correction For Odd - Math and Geometry/Math and Geometry
def diagonalSum(self, mat: List[List[int]]) -> int:
# Sum of primary (top-left to bottom-right) + secondary (top-right to bottom-left),
# while counting the center cell only once
# Note:
# 1. Walk i from 0 to n-1
# 2. Add mat[i][i] and mat[i][n-1-i] to total
# 3. If n is odd, the two diagonals share their center, subtract to avoid double counting
# Sum of all primary + secondary diagonal cells, no duplicates
# Diagonal Ex (n=3):
# [1, 2, 3] primary: 1 + 5 + 9 = 15
# [4, 5, 6] ==> secondary: 3 + 5 + 7 = 15
# [7, 8, 9] center 5 shared: 15 + 15 - 5 = 25
n = len(mat)
total = 0
# Walk rows, sum both diagonal cells per row
# tc: O(n)
for i in range(n):
# Primary Diagonal (top-left to bottom-right):
total += mat[i][i]
# Secondary Diagonal (top-right to bottom-left):
total += mat[i][n - 1 - i]
# Correct for center cell double counting (odd n only):
if n % 2 == 1:
# n//2 only used. when n is odd,
# because only odd sized matrices have a single center cell
total -= mat[n//2][n//2]
# overall: tc O(n)
# overall: sc O(1)
return total48. Rotate Image ::2:: - Medium
Topics: Array, Math, Matrix
Intro
You are given an n x n 2D matrix representing an image, rotate the image by 90 degrees (clockwise). You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation.
| Example Input | Output |
|---|---|
| matrix = [[1,2,3],[4,5,6],[7,8,9]] | [[7,4,1],[8,5,2],[9,6,3]] |
| matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]] | [[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]] |
Constraints:
n == matrix.length == matrix[i].length
1 ≤ n ≤ 20
-1000 ≤ matrix[i][j] ≤ 1000
Abstraction
Given an matrix of m * n, rotate the matrix by 90 degrees.
Pseudocode
Solution 1: Layer by Layer Rotation
1. (n = size of matrix)
2. (rings = n // 2)
3. For layer 0 to layers - 1:
a. first = layer (top-left boundary of current ring)
b. last = n - 1 - layer (bottom-right boundary of current ring)
c. ringLen = last - first (elements to rotate; last one handled by next iteration)
d. For i from 0 to ringLen - 1:
i. Save top element: top_val = matrix[first][first + i]
ii. Move left -> top: matrix[first][first + i] = matrix[last - i][first]
iii. Move bottom -> left: matrix[last - i][first] = matrix[last][last - i]
iv. Move right -> bottom: matrix[last][last - i] = matrix[first + i][last]
v. Move saved top -> right: matrix[first + i][last] = top_val
3. Repeat for all layers (outermost to innermost)
# overall: tc O(n^2)
# overall: sc O(1)Solution 1: Layer by Layer Rotation - Math and Geometry/Math and Geometry
def rotate(self, matrix: List[List[int]]) -> None:
# Rotate n x n matrix 90 degrees clockwise in place
# Note:
# 1. Process outermost layer to innermost layer
# 2. Rotate elements in four way swaps in place modification:
# Top -> Right -> Bottom -> Left -> Top
# Rotation Ex:
# [1, 2, 3] [7, 4, 1]
# [4, 5, 6] ==> [8, 5, 2]
# [7, 8, 9] [9, 6, 3]
n = len(matrix)
# Layers:
# There are n//2 layers for odd and even
# Odd layers: single center cell, does not rotate
# Even layers: no single center cell, all layers rotate
layers = n//2
# tc: O(n//2) ~ O(n)
for layer in range(layers):
# Layers:
# layer 0: outermost ring
# layer 1: next inner ring
# etc ...
# Odd Matrix Layer 0 Layer 1
# [1] = (0,0) [5] = (1,1)
# [9] = (2,2)
#
# [1, 2, 3] [1, 2, 3]
# [4, 5, 6] => [4, 6] => [5]
# [7, 8, 9] [7, 8, 9]
# Even Matrix Layer 0 Layer 1
# [1] = (0,0) [6] = (1,1)
# [16] = (3,3) [11] = (2,2)
#
# 0 = 0 1 = 1
# 3 = 4 - 1 - 0 2 = 4 - 1 - 1
#
# [1, 2, 3, 4 ] [1, 2, 3, 4 ] [ ]
# [5, 6, 7, 8 ] => [5, 8 ] => [ 6, 7, ]
# [9, 10, 11, 12] [9, 12] [ 10, 11, ]
# [13, 14, 15, 16] [13, 14, 15, 16] [ ]
# Top Left Corner:
# Start index in current ring
(layerFirstRow, layerFirstCol) = layer, layer
# Bottom Right Corner:
# end index of current ring
(layerLastRow, layerLastCol) = (n - 1 - layer, n - 1 - layer)
# Set of 4:
# For a ring of length 4,
# we only need to swap 3 elements
setOf4Cells = (layerLastCol - layerFirstCol + 1) - 1
for i in range(setOf4Cells):
# Named coordinates for the 4 cells in this "quad" (one per side)
topRow, topCol = layerFirstRow, layerFirstCol + i
rightRow, rightCol = layerFirstRow + i, layerLastCol
bottomRow, bottomCol = layerLastRow, layerLastCol - i
leftRow, leftCol = layerLastRow - i, layerFirstCol
# Only need to save top, since it's the first value we overwrite
top_val = matrix[topRow][topCol]
# Rotate clockwise: left -> top, bottom -> left, right -> bottom, saved top -> right
matrix[topRow][topCol] = matrix[leftRow][leftCol]
matrix[leftRow][leftCol] = matrix[bottomRow][bottomCol]
matrix[bottomRow][bottomCol] = matrix[rightRow][rightCol]
matrix[rightRow][rightCol] = top_val
# overall: tc O(n^2)
# overall: sc O(1)Solution 2: Rotate Trick Mirror Over Diagonal Then Reverse Rows - Math and Geometry/Math and Geometry
def rotate(self, matrix: List[List[int]]) -> None:
# Transpose (flip across diagonal) + Row Reverse (flip horizontally)
# Note:
# 1. Transpose the matrix (flip across the main diagonal)
# 2. Reverse each row (flip horizontally)
# Combined effect of these two flips = 90 degree clockwise rotation
# Rotation Ex:
# (step 1) (step 2)
# original Mirror Diagonal Reverse Rows
#
# [1, 2, 3] [1, 4, 7] [7, 4, 1]
# [4, 5, 6] ==> [2, 5, 8] ==> [8, 5, 2]
# [7, 8, 9] [3, 6, 9] [9, 6, 3]
n = len(matrix)
# Transpose the matrix:
# Swap elements across the main diagonal
# matrix[i][j] <-> matrix[j][i]
# Only swap for j > i:
# - the main diagonal (i == j) never moves
# - swapping for j > i covers every pair exactly once;
# swapping again for j < i would just undo the swap
# tc: O(n^2)
for i in range(n):
for j in range(i + 1, n):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
# Transpose Ex:
# [1, 2, 3] [1, 4, 7]
# [4, 5, 6] ==> [2, 5, 8]
# [7, 8, 9] [3, 6, 9]
# Notice how 1, 5, and 9 stay in place since they are on the main diagonal (i == j)
# But 2:4, 3:7, 6:8 are swapped with their transposed counterparts
# When the is reversed, it produces the final rotated matrix
# tc: O(n)
for i in range(n):
matrix[i].reverse()
# Reverse each row:
# Mirrors each row left-to-right
# Reverse Ex:
# [1, 4, 7] [7, 4, 1]
# [2, 5, 8] ==> [8, 5, 2]
# [3, 6, 9] [9, 6, 3]
# overall: tc O(n^2)
# overall: sc O(1)
return73. Set Matrix Zeroes ::1:: - Medium
Topics: Array, Hash Table, Matrix
Intro
Given an m x n integer matrix matrix, if an element is 0, set its entire row and column to 0's. You must do it in place. Follow up: A straightforward solution using O(mn) space is probably a bad idea. A simple improvement uses O(m + n) space, but still not the best solution. Could you devise a constant space solution?
| Example Input | Output |
|---|---|
| matrix = [[1,1,1],[1,0,1],[1,1,1]] | [[1,0,1],[0,0,0],[1,0,1]] |
| matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]] | [[0,0,0,0],[0,4,5,0],[0,3,1,0]] |
Constraints:
m == matrix.length
n == matrix[i].length
1 ≤ m, n ≤ 200
-231 ≤ matrix[i][j] ≤ 231 - 1
Abstraction
Given a matrix of m * n, if a row or column has a cell of 0 within it, that row or column must be zeroed out. Bomberman style!
Pseudocode
oh! pseudocode hasn't been written yet, try another card! :)Solution 1: Use First Matrix Row And Column As Bomberman Marker [SC Opt] - Math and Geometry/Math and Geometry
def setZeroes(self, matrix: List[List[int]]) -> None:
# Set entire row and column to 0 wherever a 0 is found, in place
# Note:
# Reuse the first row and first column of the matrix itself as the bomberman flags,
# instead of allocating separate row_flag/col_flag arrays
# 1. Before we overwrite, save a copy of the first row/col originally contained a 0
# 2. Use matrix[i][0] and matrix[0][j] to flag zero rows/cols for the REST of the matrix (i,j >= 1)
# 3. Zero out the rest of the matrix based on those flags
# 4. Re-zero the first row/col at the end, using the saved booleans
# Original matrix updated correctly: O(1) extra space
# Zeroing Ex:
# [1, 1, 1] [1, 0, 1]
# [1, 0, 1] ==> [0, 0, 0]
# [1, 1, 1] [1, 0, 1]
m, n = len(matrix), len(matrix[0])
# Presave First Row/Col for Zeros:
# Must be saved BEFORE we overwrite them as flags in Step 2
first_row_zero = False
for j in range(n):
if matrix[0][j] == 0:
first_row_zero = True
break
first_col_zero = False
for i in range(m):
if matrix[i][0] == 0:
first_col_zero = True
break
# Use First Row/Col As Bomberman Flag Markers:
# Check rows/cols >= 1 for 0, then either mark matrix[i][0] or matrix[0][j]
for i in range(1, m):
for j in range(1, n):
if matrix[i][j] == 0:
# Row 0 will serve as bomberman flag for this row
matrix[i][0] = 0
# Column 0 will serve as bomberman flag for this column
matrix[0][j] = 0 # mark column
# Zero Out Flagged Rows And Columns Using Bomberman:
# Clear rows/cols >= 1 for 0, zero entirely it a flag is present in bomberman
for i in range(1, m):
for j in range(1, n):
if matrix[i][0] == 0 or matrix[0][j] == 0:
matrix[i][j] = 0
# Re-zero first row/col if needed
# Uses the booleans saved in Step 1, since row 0 / col 0 no longer
# reliably reflect their ORIGINAL values (they were overwritten as flags)
if first_row_zero:
for j in range(n):
matrix[0][j] = 0
if first_col_zero:
for i in range(m):
matrix[i][0] = 0
# overall: tc O(m*n)
# overall: sc O(1)
return54. Spiral Matrix ::1:: - Medium
Topics: Array, Matrix, Simulation
Intro
Given an m x n matrix, return all elements of the matrix in spiral order.
| Example Input | Output |
|---|---|
| matrix = [[1,2,3],[4,5,6],[7,8,9]] | [1,2,3,6,9,8,7,4,5] |
| matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]] | [1,2,3,4,8,12,11,10,9,5,6,7] |
Constraints:
m == matrix.length
n == matrix[i].length
1 ≤ m, n ≤ 10
-100 ≤ matrix[i][j] ≤ 100
Abstraction
Given a matrix of m * n, traverse the matrix in a clockwise rotation and return that traversal order in a list.
Pseudocode
oh! pseudocode hasn't been written yet, try another card! :)Solution 1: Outer Layer To Inner With Odd Even 4 Side Check - Math and Geometry/Math and Geometry
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
# Traverse the matrix in clockwise spiral order, from outer to inner layer
# Note:
# 1. Treat each layer as a shrinking "frame" around the matrix
# 2. For each layer, walk its 4 sides in order: top -> right -> bottom -> left
# 3. Shrink inward to the next layer and repeat until all cells are visited
# Result -> flattened list of elements in spiral order
# Traversal Ex:
# [1, 2, 3 ]
# [4, 5, 6 ] ==> [1, 2, 3, 6, 9, 8, 7, 4, 5]
# [7, 8, 9 ]
# [1, 2, 3, 4 ]
# [5, 6, 7, 8 ] ==> [1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10]
# [9, 10, 11, 12]
# [13, 14, 15, 16]
# Empty check:
if not matrix or not matrix[0]:
return []
res = []
# Boundaries
m, n = len(matrix), len(matrix[0])
# Layers:
# There are ceil(min(m, n) / 2) layers
# Odd sized (shorter) dimension -> one single center row/col, still visited once
# Even sized (shorter) dimension -> no single center row/col
layers = (min(m, n) + 1) // 2
# Layer by layer traversal:
# layer 0: outermost frame
# layer 1: next inner frame
# etc ...
# tc: O(min(m,n))
for layer in range(layers):
# Top and Right: exist in odd and even
# Bottom and Left: only exists in even
# Top and right:
# [1, 2, 3]
# [4, 5, 6] layer 1 = just [5]
# [7, 8, 9]
# Bottom and left:
# [1, 2, 3, 4 ]
# [5, 6, 7, 8 ] layer 1 = [6, 7, 11, 10] (4 corner cells)
# [9, 10, 11, 12]
# [13, 14, 15, 16]
# Top Left Corner:
# Start index in current layer
(layerFirstRow, layerFirstCol) = layer, layer
# Bottom Right Corner:
# end index of current layer (inclusive)
(layerLastRow, layerLastCol) = (m - 1 - layer, n - 1 - layer)
# Rectangular matrix note: m and n can differ, so Bottom's
# existence (layerLastRow > layerFirstRow) and Left's existence
# (layerLastCol > layerFirstCol) are DIFFERENT conditions here --
# a layer can be single-row but multi-column, or vice versa.
hasBottom = layerLastRow > layerFirstRow
hasLeft = layerLastCol > layerFirstCol
# --------------------------
# Top and Right Always Exist
# Top row (left -> right)
for col in range(layerFirstCol, layerLastCol + 1):
res.append(matrix[layerFirstRow][col])
# Right column (top+1 v bottom)
for row in range(layerFirstRow + 1, layerLastRow + 1):
res.append(matrix[row][layerLastCol])
# ----------------------------------
# Bottom and Left Only Exist On Even
# Bottom row (left <- right-1)
# Only if this layer has more than one row
# (otherwise top row already visited this row)
if hasBottom:
for col in range(layerLastCol - 1, layerFirstCol - 1, -1):
res.append(matrix[layerLastRow][col])
# Left column (bottom-1 ^ top+1)
# Only if this layer has more than one column
# (otherwise right column already visited this column)
if hasLeft:
for row in range(layerLastRow - 1, layerFirstRow, -1):
res.append(matrix[row][layerFirstCol])
# overall: tc O(m*n)
# overall: sc O(m*n)
return res59. Spiral Matrix II ::1:: - Medium
Topics: Array, Matrix, Simulation
Intro
You need to create a square matrix of size n x n and fill it with numbers from 1 to n^2 following a spiral pattern.
| Example Input | Output |
|---|---|
| n = 3, | [[1, 2, 3], [8, 9, 4]. [7, 6, 5]] |
| n = 1, | [[1]] |
Constraints:
1 ≤ n ≤ 20
Abstraction
Given an integer n, create a matrix of size n * n while filling it with numbers from 1 to n^2 in a clockwise spiral order.
Pseudocode
oh! pseudocode hasn't been written yet, try another card! :)Solution 1: Layer by Layer Spiral Fill - Math and Geometry/Math and Geometry
def generateMatrix(self, n: int) -> List[List[int]]:
# Fill an n x n matrix with 1..n^2 in clockwise spiral order, layer by layer
# Note:
# Same 4-side layer walk as Spiral Matrix 54. but WRITING sequential values instead of reading existing ones
# 1. Treat each layer as a shrinking "frame" around the matrix
# 2. For each layer, walk its 4 sides in order: top -> right -> bottom -> left
# 3. Place the next number (num) at each cell as we walk, incrementing num
# 4. Shrink inward to the next layer and repeat until all cells are filled
# Result -> matrix filled with 1..n^2 in spiral order
# Fill Ex (n=3):
# [0, 0, 0] [1, 2, 3]
# [0, 0, 0] ==> [8, 9, 4]
# [0, 0, 0] [7, 6, 5]
# Fill Ex (n=4):
# [0, 0, 0, 0 ] [1, 2, 3, 4 ]
# [0, 0, 0, 0 ] ==> [5, 6, 7, 8 ]
# [0, 0, 0, 0 ] [9, 10, 11, 12]
# [0, 0, 0, 0 ] [13, 14, 15, 16]
# New Matrix:
# Initialize matrix with placeholder values
matrix = [[0] * n for _ in range(n)]
# Iterator for filling in numbers
num = 1
# Layers:
# There are ceil(n / 2) layers
# Odd n -> one single center cell, only visited once
# Even n -> no single center cell
layers = (n + 1) // 2
# Layers:
# layer 0: outermost frame
# layer 1: next inner frame
# tc: O(n)
for layer in range(layers):
# Top Left Corner:
# Start index in current layer
(layerFirstRow, layerFirstCol) = layer, layer
# Bottom Right Corner:
# end index of current layer (inclusive)
(layerLastRow, layerLastCol) = (n - 1 - layer, n - 1 - layer)
# Top and Right sides always exist in every layer
# Bottom and Left only exist if this layer is more than 1 cell wide/tall
#
# Note: since the matrix is SQUARE, layerLastRow > layerFirstRow
# and layerLastCol > layerFirstCol are always the same condition
# (both derived from the same `layer` value) -- so ONE check
# covers both Bottom and Left. This differs from Spiral Matrix
# (#54), where m != n is allowed and the two checks can disagree.
# Layer WITH inner layer (n=4, layer 0):
# spans rows 0-3 and cols 0-3 -> more than one row/col -> hasInnerLayer = True
#
# [ 1, 2, 3, 4 ]
# [ 5, ., ., 6 ] <- outer ring (layer 0) marked with numbers
# [ 7, ., ., 8 ] <- '.' cells belong to layer 1 (inner layer)
# [ 9, 10, 11, 12]
# Layer WITHOUT inner layer (n=3, layer 1 = center):
# layerFirstRow == layerLastRow AND layerFirstCol == layerLastCol
# -> single cell -> hasInnerLayer = False
#
# [ ., ., . ]
# [ ., 1, . ] <- layer 1 is just this one center cell
# [ ., ., . ]
hasLeftAndBottom = layerLastRow > layerFirstRow
# Top row, left -> right
for col in range(layerFirstCol, layerLastCol + 1):
matrix[layerFirstRow][col] = num
num += 1
# Right column, top+1 -> bottom
for row in range(layerFirstRow + 1, layerLastRow + 1):
matrix[row][layerLastCol] = num
num += 1
# Bottom row, right-1 -> left
# Only if this layer has more than one row/col
# (otherwise Step 1 already filled this row)
if hasLeftAndBottom:
for col in range(layerLastCol - 1, layerFirstCol - 1, -1):
matrix[layerLastRow][col] = num
num += 1
# Left column, bottom-1 -> top+1
# Only if this layer has more than one row/col
# (otherwise Step 2 already filled this column)
if hasLeftAndBottom:
for row in range(layerLastRow - 1, layerFirstRow, -1):
matrix[row][layerFirstCol] = num
num += 1
# overall: tc O(n^2)
# overall: sc O(n^2)
return matrix498. Diagonal Traverse ::1:: - Medium
Topics: Array, Matrix, Simulation
Intro
Given an m x n matrix mat, return an array of all the elements of the array in a diagonal order.
| Example Input | Output |
|---|---|
| mat = [[1,2,3],[4,5,6],[7,8,9]] | [1,2,4,7,5,3,6,8,9] |
| mat = [[1,2],[3,4]] | [1,2,3,4] |
Constraints:
m == matrix.length
n == matrix[i].length
1 ≤ m * n ≤ 10^4
1 ≤ m, n ≤ 10
-10^5 ≤ matrix[i][j] ≤ 10^5
Abstraction
Given an m x n matrix, starting at the top left cell, traverse the matrix in a diagonal order alternating between the two 2 diagonals (up to the right and up to the left), and return a list of elements from that traversal diagonal order.
Pseudocode
oh! pseudocode hasn't been written yet, try another card! :)Solution 1: Boundary Bouncing Simulation - Math and Geometry/Math and Geometry
def findDiagonalOrder(self, mat: List[List[int]]) -> List[int]:
# Walk the matrix diagonal by diagonal,
# alternating diagonal by tracking boundaries of matrix
# and 'bouncing' off once we walk off the edge of the matrix
# Note:
# 1. We either walk diagonal up to right or down to left
# 2. Track current (row, col) and a direction flag (movingUp)
# 3. Append the current cell, then take one step in that direction
# 4. When a step would go off the matrix edge, "bounce": shift to
# the next diagonal (one cell down or right) and flip direction
# 5. Edge checks are ordered so a corner cell resolves correctly
# (e.g. top-right corner while moving up hits the "last column"
# check first, so it drops down rather than trying to go right)
# All m*n elements visited exactly once, output built directly in diagonal order
# Diagonal Ex (m=3, n=3):
# *
# [1, 2, 3] *
# * [4, 5, 6] ==> [1, 2, 4, 7, 5, 3, 6, 8, 9]
# [7, 8, 9] eof
# *
# Where '*' marks where a diagonal walk hits a matrix edge and "bounces"
# shifts to the next diagonal + flips direction,
# instead of continuing straight off the edge of the matrix:
# top * -> cell 1 hits the top edge -> bounces DOWN
# left * -> cell 4 hits the left edge -> bounces UP
# right * -> cell 3 hits the right edge -> bounces DOWN
# bottom * -> cell 8 hits the bottom edge -> bounces UP
m, n = len(mat), len(mat[0])
res = []
row, col = 0, 0
movingUp = True
# Simulate the diagonal walk, one cell at a time
# while using the bouncing trick when you walk off of the matrix boundary
# tc: O(m*n)
for _ in range(m * n):
# Add current cell to output,
# now diagonal walk to the next cell in the current direction,
# and bounce off the edge if needed
res.append(mat[row][col])
# Moving Up To Right (row-1, col+1)
if movingUp:
# We need to protect the top row and right column from being walked off of:
# Hit right edge:
# drop down by 1 row,
# start next diagonal downward
if col == n - 1:
row += 1
movingUp = False
# Hit top edge:
# shift right by 1 column,
# start next diagonal downward
elif row == 0:
col += 1
movingUp = False
# Still within matrix:
# Continue diagonal walk up and to the right
else:
row -= 1
col += 1
# Moving Down To Left (row+1, col-1)
else:
# We need to protect the bottom row and left column from being walked off of:
# Hit bottom edge:
# shift right, start next diagonal upward
if row == m - 1:
col += 1
movingUp = True
# Hit left edge:
# drop down, start next diagonal upward
elif col == 0:
row += 1
movingUp = True
# Still within matrix:
# Continue diagonal walk down and to the left
else:
# regular step: continue down-left
row += 1
col -= 1
# overall: tc O(m*n)
# overall: sc O(m*n)
return res1424. Diagonal Traverse II ::1:: - Medium
Topics: Array, Sorting, Heap (Priority Queue)
Intro
Given a 2D integer array nums, return all elements of nums in diagonal order as shown in the below images.
| Example Input | Output |
|---|---|
| nums = [[1,2,3],[4,5,6],[7,8,9]] | [1,4,2,7,5,3,8,6,9] |
| nums = [[1,2,3,4,5],[6,7],[8],[9,10,11],[12,13,14,15,16]] | [1,6,2,8,7,3,9,4,12,10,5,13,11,14,15,16] |
Constraints:
1 ≤ nums.length ≤ 10^5
1 ≤ nums[i].length ≤ 10^5
1 ≤ sum(nums[i].length) ≤ 10^5
1 ≤ nums[i][j] ≤ 10^5
Abstraction
Given an unfilled matrix of m x n, starting at the top left cell, traverse the matrix in a diagonal order alternating between the two 2 diagonals (up to the right and up to the left), and return a list of elements from that traversal diagonal order.
Pseudocode
oh! pseudocode hasn't been written yet, try another card! :)Solution 1: Diagonal Bucketing via Reverse Row Walk - Math and Geometry/Math and Geometry
def findDiagonalOrder(self, nums: List[List[int]]) -> List[int]:
# Group elements by diagonal (row + col),
# output larger row elements first within each diagonal,
# and diagonals in increasing order
# Uneven Matrix:
# Rows will have different lengths, so we cannot edge bounce,
# so instead, bucket every element by its diagonal index (row + col),
# then concatenate buckets in diagonal order.
# Avoid an extra reverse pass:
# Walking rows from LAST to FIRST (instead of first to last)
# means that within a given diagonal, the higher row element
# gets appended to its bucket BEFORE the lower-row element,
# giving the output the problem wants
# Note:
# 1. Walk i from last row to first row; within each row, j left to right
# 2. Bucket nums[i][j] under key (i + j)
# 3. Track the largest diagonal index seen (maxDiagonal)
# 4. Concatenate buckets for d = 0 .. maxDiagonal, in order
# Flat list of all elements in correct diagonal order
# Diagonal Ex:
#
# [1, 2, 3]
# [4, 5, 6] ==> [1, 4, 2, 7, 5, 3, 8, 6, 9]
# [7, 8, 9]
# diagonal 0: {1} -> [1]
# diagonal 1: {4, 2} -> [4, 2] (higher row first, 4 = row 1, 2 = row 0)
# diagonal 2: {7, 5, 3} -> [7, 5, 3]
# diagonal 3: {8, 6} -> [8, 6]
# diagonal 4: {9} -> [9]
m = len(nums)
groups = defaultdict(list)
# Track the largest diagonal index seen,
# so we know how many buckets to concatenate
maxDiagonal = 0
# Bucket every element by diagonal,
# walking rows bottom-up
# tc: O(m*n)
for i in range(m - 1, -1, -1):
row = nums[i]
for j in range(len(row)):
diagonal = i + j
groups[diagonal].append(row[j])
maxDiagonal = max(maxDiagonal, diagonal)
# Concatenate buckets in increasing diagonal order
res = []
# tc: O(m*n)
for d in range(maxDiagonal + 1):
res.extend(groups[d])
# overall: tc O(m*n)
# overall: sc O(m*n)
return resSolution 2: BFS Queue Traversal - Math and Geometry/Math and Geometry
def findDiagonalOrder(self, nums: List[List[int]]) -> List[int]:
# Traverse the jagged array in diagonal order using BFS, where
# FIFO queue order naturally interleaves cells into diagonal order
# Note:
# No explicit diagonal index (row + col) is ever computed. Instead,
# each cell enqueues its two possible "next" cells, and because a
# queue processes items in the order they were added (FIFO), cells
# from all diagonals interleave automatically in the right order --
# no bucketing, no dict, no second pass needed.
# 1. Start at (0, 0), the only starting point of any diagonal walk
# 2. Pop a cell, record its value
# 3. If this cell is the FIRST in its row (col == 0), enqueue the
# cell directly below it -- this starts the NEXT diagonal
# 4. Always enqueue the cell to the right (same diagonal, one step
# further along) if it exists
# 5. Repeat until the queue is empty
# Result -> single-pass diagonal-order traversal, no auxiliary
# grouping structure required
# Diagonal Ex:
# [1, 2, 3]
# [4, 5, 6] ==> [1, 4, 2, 7, 5, 3, 8, 6, 9]
# [7, 8, 9]
# Seed the queue with the starting cell
queue = deque([(0, 0)])
res = []
# Process cells in FIFO order, enqueueing next cells
# tc: O(m*n)
while queue:
row, col = queue.popleft()
res.append(nums[row][col])
# First cell of this row -> start next diagonal (row below)
if col == 0 and row + 1 < len(nums):
queue.append((row + 1, col))
# Next cell along the same diagonal (one column right)
if col + 1 < len(nums[row]):
queue.append((row, col + 1))
# overall: tc O(m*n)
# overall: sc O(m*n + w) where w is the widest diagonal
return res1329. Sort the Matrix Diagonally ::1:: - Medium
Topics: Array, Sorting, Matrix
Intro
A matrix diagonal is a diagonal line of cells starting from some cell in either the topmost row or leftmost column and going in the bottom right direction until reaching the matrix's end. For example, the matrix diagonal starting from mat[2][0], where mat is a 6 x 3 matrix, includes cells mat[2][0], mat[3][1], and mat[4][2]. Given an m x n matrix mat of integers, sort each matrix diagonal in ascending order and return the resulting matrix.
| Example Input | Output |
|---|---|
| mat = [[3,3,1,1],[2,2,1,2],[1,1,1,2]] | [[1,1,1,1],[1,2,2,2],[1,2,3,3]] |
Constraints:
m == mat.length
n == mat[i].length
1 ≤ m, n ≤ 100
1 ≤ mat[i][j] ≤ 100
Abstraction
Given an m x n matrix, group cells that share the same diagonal (row - col) either (top-left or bottom-right diagonal), and within each diagonal, sort in ascending order, and then write each sorted group back into its diagonal going from top left to bottom right reading order.
Pseudocode
oh! pseudocode hasn't been written yet, try another card! :)Solution 1: Bucket by Diagonal Key Then Sort And Refill - Math and Geometry/Math and Geometry
def diagonalSort(self, mat: List[List[int]]) -> List[List[int]]:
# Sort each top left to bottom right diagonal independently,
# then write the sorted values back into their diagonal
# Shared (row - col)
# Every cell on the same diagonal shares the value (row - col),
# so moving one step down-right increases both row and col by 1,
# so their difference never changes.
# This lets us bucket cells by diagonal WITHOUT
# tracking start position or diagonal length.
# Note:
# 1. Bucket every cell's value under key (row - col)
# 2. Sort each bucket ascending
# 3. Walk the matrix again in the SAME top-left to bottom-right
# order used to bucket, popping the smallest remaining value
# from each diagonal's bucket as we go
# Matrix with every diagonal individually sorted ascending
# Diagonal key Ex:
# [3, 3, 1, 1] row col: 0 -1 -2 -3
# [2, 2, 1, 2] 1 0 -1 -2
# [1, 1, 1, 2] 2 1 0 -1
# diagonal 0: {3, 2, 1} (mat[0][0], mat[1][1], mat[2][2]) -> sorted [1, 2, 3]
# diagonal -1: {3, 1, 2} (mat[0][1], mat[1][2], mat[2][3]) -> sorted [1, 2, 3]
m, n = len(mat), len(mat[0])
groups = defaultdict(list)
# Bucket every cell by diagonal key (row - col)
# tc: O(m*n)
for i in range(m):
for j in range(n):
groups[i - j].append(mat[i][j])
# Sort each diagonal bucket ascending
# tc: O(m*n log(m*n)) worst case (single long diagonal)
for key in groups:
# reverse so pop() gives smallest first
groups[key].sort(reverse=True)
# Refill matrix:
# Popping smallest remaining value per diagonal,
# walking in the same order guarantees each diagonal's cells
# are refilled top-left to bottom-right with the smallest value first
# tc: O(m*n)
for i in range(m):
for j in range(n):
mat[i][j] = groups[i - j].pop()
# overall: tc O(m*n log(m*n))
# overall: sc O(m*n)
return mat1861. Rotating the Box ::1:: - Medium
Topics: Array, Sorting, Matrix
Intro
You are given an m x n matrix of characters boxGrid representing a side-view of a box. Each cell of the box is one of the following: A stone '#' A stationary obstacle '*' Empty '.' The box is rotated 90 degrees clockwise, causing some of the stones to fall due to gravity. Each stone falls down until it lands on an obstacle, another stone, or the bottom of the box. Gravity does not affect the obstacles' positions, and the inertia from the box's rotation does not affect the stones' horizontal positions. It is guaranteed that each stone in boxGrid rests on an obstacle, another stone, or the bottom of the box. Return an n x m matrix representing the box after the rotation described above.
| Example Input | Output |
|---|---|
| boxGrid = [["#",".","#"]] | [["."], ["#"], ["#"]] |
Constraints:
m == boxGrid.length
n == boxGrid[i].length
1 ≤ m, n ≤ 100
boxGrid[i][j] is either '#', '*', or '.'.
Abstraction
Given an m x n matrix of characters representing a side-view of a box, rotate the box 90 degrees clockwise and simulate gravity on the stones ('#') so that they fall down until they land on an obstacle ('*'), another stone, or the bottom of the box. Return the resulting n x m matrix after rotation and gravity simulation.
Pseudocode
oh! pseudocode hasn't been written yet, try another card! :)Solution 1: Gravity Before Rotate Via Right Shift Simulation - Math and Geometry/Math and Geometry
def rotateTheBox(self, boxGrid: List[List[str]]) -> List[List[str]]:
# Simulate gravity within each row FIRST, then rotate 90 clockwise
# Mimicking Gravity:
# Rotating first and THEN simulating "downward" gravity on a rotated grid is awkward,
# but since a stone's ROW never changes because of rotation
# (rotation only moves stones between columns),
# and "falling down" in the FINAL rotated box is
# equivalent to "sliding right" in the ORIGINAL row,
# since a clockwise rotation maps each original row to a column read
# top to bottom in reverse.
# So we can settle gravity BEFORE rotating,
# using a simple right-to-left two-pointer slide per row
# allowing us to avoid the need to simulate gravity on a rotated structure.
# Note:
# 1. For each row, walk right to left with a `write` pointer
# tracking the next open rightmost slot
# 2. Obstacles reset the write pointer just past themselves
# (stones can't fall through an obstacle)
# 3. Stones get moved to `write`, and write shifts left by one
# 4. After all rows have settled, rotate the grid 90 clockwise
# using standard index mapping: result[j][m-1-i] = grid[i][j]
# n x m matrix, rotated with gravity correctly applied
# Gravity + Rotate Ex:
# ["#", ".", "#"] --gravity--> [".", "#", "#"] --rotate--> ["."]
# ["#"]
# ["#"]
m, n = len(boxGrid), len(boxGrid[0])
# Apply gravity rightward within each row
# tc: O(m*n)
for row in boxGrid:
write = n - 1
for read in range(n - 1, -1, -1):
# Obstacle: stones can't fall past this point
if row[read] == '*':
write = read - 1
# Stone: move it to the next open rightmost slot
elif row[read] == '#':
row[read] = '.'
row[write] = '#'
write -= 1
# '.' cells: nothing to do, keep scanning
# Rotate the gravity-settled grid 90 degrees clockwise
result = [['.'] * m for _ in range(n)]
# tc: O(m*n)
for i in range(m):
for j in range(n):
result[j][m - 1 - i] = boxGrid[i][j]
# overall: tc O(m*n)
# overall: sc O(m*n)
return result1886. Determine Whether Matrix Can Be Obtained By Rotation ::1:: - Easy
Topics: Array, Matrix
Intro
Given two n x n binary matrices mat and target, return true if it is possible to make mat equal to target by rotating mat in 90-degree increments, or false otherwise.
| Example Input | Output |
|---|---|
| mat = [[0,1],[1,0]], target = [[1,0],[0,1]] | true |
| mat = [[0,1],[1,1]], target = [[1,0],[0,1]] | false |
| mat = [[0,0,0],[0,1,0],[1,1,1]], target = [[1,1,1],[0,1,0],[0,0,0]] | true |
Constraints:
n == mat.length == target.length
n == mat[i].length == target[i].length
1 ≤ n ≤ 10
mat[i][j] and target[i][j] are either 0 or 1.
Abstraction
Given two n x n matrixes, determine if one can be transformed into the other by rotating it in 90-degree increments.
Pseudocode
oh! pseudocode hasn't been written yet, try another card! :)Solution 1: Rotate Up To 4 Times And Compare - Math and Geometry/Math and Geometry
def findRotation(self, mat: List[List[int]], target: List[List[int]]) -> bool:
# Check if mat can be rotated (0, 90, 180, or 270 degrees) to equal target
# 4 Orientations:
# There are only 4 possible orientations of a square matrix under 90-degree rotation,
# so instead of reasoning about which rotation might work,
# just PRODUCE all 4 and compare each to target directly.
# Reuses the exact same rotate-in-place logic from
# Rotate Image 48. transpose + reverse each row.
# Note:
# 1. Check the un-rotated mat against target first
# 2. Rotate mat 90 degrees, check again
# 3. Repeat for 180 and 270 (i.e. rotate 3 times total, checking
# after each rotation)
# 4. If none of the 4 states match, no rotation can make them equal
# True if any of mat's 4 rotation states equals target
# Rotation states Ex:
# mat: target:
# [0, 1] [1, 0]
# [1, 0] [0, 1]
#
# rotate mat 90: [1, 0] <- matches target -> True
# [0, 1]
n = len(mat)
# Try 4 Rotation States (0, 90, 180, 270)
# tc: O(n^2) per rotation/comparison
for _ in range(4):
#
if mat == target:
return True
# Rotate mat 90 degrees clockwise in place (Transpose + Reverse)
for i in range(n):
for j in range(i + 1, n):
mat[i][j], mat[j][i] = mat[j][i], mat[i][j]
for row in mat:
row.reverse()
# overall: tc O(n^2)
# overall: sc O(1)
return False289. Game of Life ::1:: - Medium
Topics: Array, Matrix, Simulation
Intro
According to Wikipedia's article: "The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970." The board is made up of an m x n grid of cells, where each cell has an initial state: live (represented by a 1) or dead (represented by a 0). Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article): Any live cell with fewer than two live neighbors dies as if caused by under-population. Any live cell with two or three live neighbors lives on to the next generation. Any live cell with more than three live neighbors dies, as if by over-population. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction. The next state of the board is determined by applying the above rules simultaneously to every cell in the current state of the m x n grid board. In this process, births and deaths occur simultaneously. Given the current state of the board, update the board to reflect its next state. Note that you do not need to return anything. Follow up: Could you solve it in-place? Remember that the board needs to be updated simultaneously: You cannot update some cells first and then use their updated values to update other cells. In this question, we represent the board using a 2D array. In principle, the board is infinite, which would cause problems when the active area encroaches upon the border of the array (i.e., live cells reach the border). How would you address these problems?
| Example Input | Output |
|---|---|
| board = [[0,1,0],[0,0,1],[1,1,1],[0,0,0]] | [[0,0,0],[1,0,1],[0,1,1],[0,1,0]] |
Constraints:
m == board.length
n == board[i].length
1 ≤ m, n ≤ 25
board[i][j] is 0 or 1.
Abstraction
Given an m x n matrix, represented by live (1) and dead (0) cells, compute each cell's next state simultaneously based on its 8 neighbors's current state, using 4 fixed rules, without letting already updated cells influence neighbors that haven't been processed yet.
Pseudocode
oh! pseudocode hasn't been written yet, try another card! :)Solution 1: In Place State Encoding - Math and Geometry/Math and Geometry
def gameOfLife(self, board: List[List[int]]) -> None:
# Update board to its next Game of Life generation, in place
# Test:
# Simultaneous update in place is tricky: once you overwrite a cell,
# its neighbors' neighbor-counts would be wrong if they read the
# NEW value instead of the ORIGINAL value. Fix: encode both the
# original and next state into the SAME cell using extra integer
# values, then do a final decode pass.
# 0 -> 1: cell was dead, becomes live (encoded as 2)
# 1 -> 0: cell was live, becomes dead (encoded as -1)
# 0 -> 0: cell was dead, stays dead (encoded as 0, unchanged)
# 1 -> 1: cell was live, stays live (encoded as 1, unchanged)
# While counting neighbors mid-pass, checking `cell in (1, -1)`
# correctly identifies "was originally live," since -1 hasn't been
# decoded yet and still reads as "was live" via its encoding.
# Note:
# 1. For each cell, count live neighbors using ORIGINAL-state check
# 2. Apply the 4 rules, encoding transitions instead of overwriting directly
# 3. Decode all cells back to 0/1 in a final pass
# Result -> board updated in place, O(1) extra space
m, n = len(board), len(board[0])
# Encode transitions based on original state + neighbor count
# tc: O(m*n * 8) -> O(m*n)
for i in range(m):
for j in range(n):
liveNeighbors = 0
for di in (-1, 0, 1):
for dj in (-1, 0, 1):
if di == 0 and dj == 0:
continue
ni, nj = i + di, j + dj
if 0 <= ni < m and 0 <= nj < n:
# abs() catches originally-live cells even if
# they were already encoded as -1 this pass
if abs(board[ni][nj]) == 1:
liveNeighbors += 1
if board[i][j] == 1:
if liveNeighbors < 2 or liveNeighbors > 3:
board[i][j] = -1 # live -> dead
else:
if liveNeighbors == 3:
board[i][j] = 2 # dead -> live
# Decode encoded values back to 0/1
# tc: O(m*n)
for i in range(m):
for j in range(n):
if board[i][j] > 0:
board[i][j] = 1
else:
board[i][j] = 0
# overall: tc O(m*n)
# overall: sc O(1)2482. Difference Between Ones and Zeros in Row and Column ::1:: - Medium
Topics: Array, Matrix, Simulation
Intro
You are given a 0-indexed m x n binary matrix grid. A 0 indexed m x n difference matrix diff is created with the following procedure: Let the number of ones in the ith row be onesRowi. Let the number of ones in the jth column be onesColj. Let the number of zeros in the ith row be zerosRowi. Let the number of zeros in the jth column be zerosColj. diff[i][j] = onesRowi + onesColj - zerosRowi - zerosColj Return the difference matrix diff.
| Example Input | Output |
|---|---|
| grid = [[0,1,1],[1,0,1],[0,0,1]] | [[0,0,4],[0,0,4],[-2,-2,2]] |
| grid = [[1,1,1],[1,1,1]] | [[5,5,5],[5,5,5]] |
Constraints:
m == grid.length
n == grid[i].length
1 ≤ m, n ≤ 10^5
1 ≤ m * n ≤ 10^5
grid[i][j] is either 0 or 1.
Abstraction
Given an m x n matrix, compute for every cell (i, j) the value: (count of 1s in row i) + (count of 1s in column j) - (count of 0s in row i) - (count of 0s in column j), and using precomputed row/column summaries instead of rescanning per cell.
Pseudocode
oh! pseudocode hasn't been written yet, try another card! :)Solution 1: Precompute Row/Column Ones Counts - Math and Geometry/Math and Geometry
def onesMinusZeros(self, grid: List[List[int]]) -> List[List[int]]:
# For every cell, compute (ones in its row + ones in its col)
# minus (zeros in its row + zeros in its col)
# Note:
# Precompute the count of 1s in every row and every column ONCE,
# then derive zero counts and the final diff value for every cell
# in a single pass -- avoids rescanning a row/column for each
# individual cell (same pattern as Lucky Numbers: precompute
# row/col summaries first, then a combining pass).
# 1. onesRow[i] = count of 1s in row i
# 2. onesCol[j] = count of 1s in column j
# 3. zerosRow[i] = n - onesRow[i] (total cells in row i minus ones)
# 4. zerosCol[j] = m - onesCol[j] (total cells in col j minus ones)
# 5. diff[i][j] = onesRow[i] + onesCol[j] - zerosRow[i] - zerosCol[j]
# Result -> m x n difference matrix
# Diff Ex:
# [0, 1, 1]
# [1, 0, 1] ==> diff[0][0] = onesRow[0] + onesCol[0]
# [0, 0, 1] - zerosRow[0] - zerosCol[0]
# = 2 + 1 - 1 - 2 = 0
m, n = len(grid), len(grid[0])
onesRow = [0] * m
onesCol = [0] * n
# Count ones per row and per column
# tc: O(m*n)
for i in range(m):
for j in range(n):
if grid[i][j] == 1:
onesRow[i] += 1
onesCol[j] += 1
# Combine per-cell using precomputed row/col counts
# zerosRow[i] = n - onesRow[i], zerosCol[j] = m - onesCol[j]
diff = [[0] * n for _ in range(m)]
# tc: O(m*n)
for i in range(m):
for j in range(n):
zerosRow = n - onesRow[i]
zerosCol = m - onesCol[j]
diff[i][j] = onesRow[i] + onesCol[j] - zerosRow - zerosCol
# overall: tc O(m*n)
# overall: sc O(m+n)
return diff