Jc-alt logo
jc
10 min read
#data structures and algorithms

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!

48. Rotate Image ::1:: - 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 InputOutput
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 2D array, rotate the 2D array 90 degrees.

Pseudocode

  text will go here

Solution 1: Layer by Layer Rotation - Math and Geometry/Math and Geometry

    def rotate(self, matrix: List[List[int]]) -> None:
        
        # Note:
        # Rotate n x n matrix 90 degrees clockwise in place
        # 1. Process the matrix layer by layer (outermost to innermost)
        # 2. For each layer, rotate elements in four way swaps:
        #       Top -> Right -> Bottom -> Left -> Top
        # 3. Repeat for all layers
        # Result -> No extra matrix allocation, in place modification

        # Example (n=3, outer layer rotation):
        # [1, 2, 3]         [7, 4, 1]
        # [4, 5, 6]   --->  [8, 5, 2]
        # [7, 8, 9]         [9, 6, 3]

        n = len(matrix)

        # There are n//2 layers
        # Odd sized matrix -> one single center cell, inner does not rotate
        # Even sized matrix -> no single center cell, all layers rotate
        layers = n//2

        for layer in range(layers):

            # first -> start index of current ring (top-left corner)
            first = layer

            # last -> end index of current ring (bottom-right corner)

            # layer 1:
            # [ 1, 1, 1, 1, 1, 1]
            # [ 1, 1, 1, 1, 1, 1]
            #     f^.       l^
            # ... 
            # ...
            last = n - 1 - layer

            # length of current ring - 1
            # notice we only need 3 swaps as 4th swap is 1 swap of next switch
            # [1,  2,  3,  4 ]
            # [5,  6,  7,  8 ]
            # [9,  10, 11, 12]
            # [13, 14, 15, 16]
            
            # we only swap, 1, 2, 3, as 4 will be taken care of 
            # by the next iteration, therefore ringLen is -1 its actual length
            # thus ring len of 4 goes to 3
            ringLen = last - first            

            # iterate -> 0 to ringLen-1 
            for i in range(ringLen):

                # left -> top
                # bottom -> left
                # right -> bottom
                # saved top -> right

                # save top
                top_val = matrix[first][first + i]

                # top: left right via row    || left: bottom up via column
                matrix[first][first + i] = matrix[last - i][first]

                # left: bottom up via column ||  bottom: right left via row
                matrix[last - i][first] = matrix[last][last - i]

                # bottom: right left via row ||  right: top down via column
                matrix[last][last - i] = matrix[first + i][last]

                # right: top down via column ||  top: left right via row
                matrix[first + i][last] = top_val

        # overall: time complexity O(n^2)
        # overall: space complexity O(1)

Solution 2: Transpose And Reverse - Math and Geometry/Math and Geometry

    def rotate(self, matrix: List[List[int]]) -> None:
        
        n = len(matrix)

        # -----------------------------
        # Step 1: Transpose the matrix
        # Swap elements across the main diagonal
        # matrix[i][j] <-> matrix[j][i]
        # Only swap for j > i to avoid double swap
        # -----------------------------
        for i in range(n):
            for j in range(i + 1, n):
                matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]

        # -----------------------------
        # Step 2: Reverse each row
        # Reversing rows after transpose achieves 90° clockwise rotation
        # -----------------------------
        for i in range(n):
            matrix[i].reverse()

        # overall: time complexity O(n^2)
        # overall: space complexity O(1) in-place

73. 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 InputOutput
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 2D array, if a cell has 0, zero out its adjacent row and column.

Pseudocode

  text will go here

Solution 1: Brute Force O(m*n) Space - Math and Geometry/Math and Geometry

    def setZeroes(self, matrix: List[List[int]]) -> None:
        
        # Note:
        # Separate iterating from writing via creating copy of matrix using O(m*n)
        # We read over the copy while updating the original in place
        # 1. Iterate through original matrix
        # 2. If cell is 0, set corresponding row and column in copy to 0
        # 3. Copy back to original matrix
        # Result -> original matrix set correctly in place

        m, n = len(matrix), len(matrix[0])
        
        # Create a copy
        copy = [row[:] for row in matrix]  # O(m*n) space

        for i in range(m):
            for j in range(n):
                if copy[i][j] == 0:
                    # Zero entire row
                    for col in range(n):
                        matrix[i][col] = 0
                    # Zero entire column
                    for row in range(m):
                        matrix[row][j] = 0

        # overall: time complexity O(m*n*(m+n)) due to nested zeroing loops
        # overall: space complexity O(m*n) for copy

Solution 2: Using O(m+n) Space - Math and Geometry/Math and Geometry

    def setZeroes(self, matrix: List[List[int]]) -> None:
        
        # Note:
        # Separate iterating from writing via creating copy of matrix using O(m+n)
        # Use two arrays to store flags to track zero rows and zero columns
        # 1. row_flag[m] -> True if row i should be zero
        # 2. col_flag[n] -> True if column j should be zero
        # 3. Iterate matrix, set flags
        # 4. Iterate matrix again, zero out flagged rows and columns
        # Result -> original matrix set correctly in place


        m, n = len(matrix), len(matrix[0])
        row_flag = [False] * m
        col_flag = [False] * n

        # Flag zero rows and columns
        for i in range(m):
            for j in range(n):
                if matrix[i][j] == 0:
                    row_flag[i] = True
                    col_flag[j] = True

        # Zero out flagged rows and columns in original matrix
        for i in range(m):
            for j in range(n):
                if row_flag[i] or col_flag[j]:
                    matrix[i][j] = 0

        # overall: time complexity O(m*n)
        # overall: space complexity O(m+n)

Solution 3: Constant Space O(1) - Math and Geometry/Math and Geometry

    def setZeroes(self, matrix: List[List[int]]) -> None:
        
        # Note:
        # Separate iterating from writing via creating copy of matrix using O(m+n)
        # We use the first row and first column of the matrix itself
        # 1. row_flag[m] -> True if row i should be zero
        # 2. col_flag[n] -> True if column j should be zero
        # 4. Iterate matrix again, zero out flagged rows and columns
        # Result -> original matrix set correctly in place

        # Boundaries
        m, n = len(matrix), len(matrix[0])

        # Pre check if first row has any zeros
        first_row_zero = False
        for j in range(n):
            if matrix[0][j] == 0:
                first_row_zero = True
                break

        # Pre check if first column has any zeros
        first_col_zero = False
        for i in range(m):
            if matrix[i][0] == 0:
                first_col_zero = True
                break

        # Set zero flags using first row and column
        for i in range(1, m):
            for j in range(1, n):
                if matrix[i][j] == 0:
                    matrix[i][0] = 0  # mark row
                    matrix[0][j] = 0  # mark column

        # Using zero flags, zero out rows and cols
        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

        # Afterwards:
        # Zero out the first row if needed
        if first_row_zero:
            for j in range(n):
                matrix[0][j] = 0

        # Afterwards:
        # Zero out the first column if needed
        if first_col_zero:
            for i in range(m):
                matrix[i][0] = 0

        # overall: time complexity O(m*n)
        # overall: space complexity O(1)

54. 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 InputOutput
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 2D array, return a list of all the elements in the matrix by iterating in a clockwise rotation.

Pseudocode

  text will go here

Solution 1: Layer by Layer Spiral Traversal - Math and Geometry/Math and Geometry

    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        
        # Note:
        # Traverse the matrix in clockwise spiral order using layers
        # 1. Treat each layer as a shrinking “frame” around the matrix
        # 2. For each layer, traverse top row, right column, bottom row, left column
        # 3. Increment layer and repeat until all elements are processed

        # Empty check
        if not matrix or not matrix[0]:
            return []

        res = []

        # boundaries
        m, n = len(matrix), len(matrix[0])

        # number of layer -> min between rows and columns
        # There are (min(row, col) // 2) layers
        # Odd sized matrix -> one single center cell
        # Even sized matrix -> no single center cell
        layers = (min(m, n) + 1) // 2

        for layer in range(layers):

            # boundaries of current layer
            first_row = layer
            first_col = layer

            # inclusive via last element

            # layer 1:
            # [ 1, 1, 1, 1, 1, 1]
            # [ 1, 1, 1, 1, 1, 1]
            #     f^.       l^
            # ... 
            # ...
            last_row = m - 1 - layer
            last_col = n - 1 - layer

            # Top and Right sides always exist in every layer
            # Bottom and left may not always exist

            # Top row: left -> right
            for col in range(first_col, last_col + 1):
                res.append(matrix[first_row][col])

            # Right column: top+1 -> bottom
            for row in range(first_row + 1, last_row + 1):
                res.append(matrix[row][last_col])

            # Bottom row: right-1 -> left (if more than one row)
            if last_row > first_row:
                for col in range(last_col - 1, first_col - 1, -1):
                    res.append(matrix[last_row][col])

            # Left column: bottom-1 -> top+1 (if more than one column)
            if last_col > first_col:
                for row in range(last_row - 1, first_row, -1):
                    res.append(matrix[row][first_col])

        # overall: time complexity O(m*n)
        # overall: space complexity O(m*n) for output list
        return res