Leetcode 矩阵 1

题号直达: 73 矩阵置零 | 54 螺旋矩阵 | 48 旋转图像 | 240 搜索二维矩阵 II

LeetCode 73. 矩阵置零 ↑

题目

给定一个 m × n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。

进阶: 直观做法可用 O(mn) 或 O(m+n) 额外空间;能否想出仅使用常量空间的解决方案?

测试样例

示例 1:
输入:matrix = [[1,1,1],[1,0,1],[1,1,1]]
输出:[[1,0,1],[0,0,0],[1,0,1]]

示例 2:
输入: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]]

提示: 1 ≤ m, n ≤ 200,-2^31 ≤ matrix[i][j] ≤ 2^31 - 1。

代码(Python)解法一:行列标记数组

class Solution(object):
    def setZeroes(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: None Do not return anything, modify matrix in-place instead.
        """
        row = [0] * len(matrix)
        col = [0] * len(matrix[0])

        for i in range(0, len(matrix)):
            for j in range(0, len(matrix[0])):
                if matrix[i][j] == 0:
                    row[i] = 1
                    col[j] = 1

        for i in range(0, len(row)):
            if row[i] == 1:
                matrix[i] = [0] * len(matrix[0])

        for j in range(0, len(col)):
            if col[j] == 1:
                for i in range(0, len(row)):
                    matrix[i][j] = 0

思路解析(解法一)

  • 思路:用两个一维数组 row、col 分别标记「哪一行、哪一列需要整行/整列置零」。第一遍遍历矩阵,遇到 matrix[i][j] == 0 就令 row[i] = 1、col[j] = 1。然后先按行:若 row[i] == 1 则把第 i 行整行赋为 0(这里用 matrix[i] = [0] * len(matrix[0]) 直接替换该行);再按列:若 col[j] == 1 则遍历所有行把 matrix[i][j] 置 0。
  • 复杂度:时间复杂度 O(mn),空间复杂度 O(m+n)(两个标记数组)。满足原地修改矩阵的要求;若进阶要求 O(1) 额外空间,见解法二。

代码(Python)解法二:用第一行、第一列做标记(O(1) 空间)

class Solution(object):
    def setZeroes(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: None Do not return anything, modify matrix in-place instead.
        """
        m = len(matrix)
        n = len(matrix[0])
        flag_col0 = False

        # 第一遍遍历:标记需要置零的行和列
        for i in range(m):
            if matrix[i][0] == 0:
                flag_col0 = True

            for j in range(1, n):
                if matrix[i][j] == 0:
                    matrix[i][0] = matrix[0][j] = 0

        # 第二遍遍历:根据标记置零(从后往前)
        for i in range(m - 1, -1, -1):
            for j in range(1, n):
                if matrix[i][0] == 0 or matrix[0][j] == 0:
                    matrix[i][j] = 0

            if flag_col0:
                matrix[i][0] = 0

思路解析(解法二)

  • 思路:用矩阵的第一行 matrix[0][j] 标记第 j 列是否要置零,用第一列 matrix[i][0] 标记第 i 行是否要置零。这样只需 O(1) 额外空间。但第一行和第一列会重叠在 matrix[0][0],所以单独用变量 flag_col0 表示「第一列是否有 0」;matrix[0][0] 只表示「第一行是否有 0」。
  • 第一遍:遍历时若 matrix[i][j] == 0(j ≥ 1),则令 matrix[i][0] = matrix[0][j] = 0;若 matrix[i][0] == 0 则设 flag_col0 = True。
  • 第二遍从后往前:避免先改第一行/第一列导致标记被覆盖。对 i 从 m-1 到 0、j 从 1 到 n-1,若 matrix[i][0] == 0 或 matrix[0][j] == 0 则 matrix[i][j] = 0;最后若 flag_col0 为真则把 matrix[i][0] 置 0。
  • 复杂度:时间复杂度 O(mn),空间复杂度 O(1),满足进阶要求。

LeetCode 54. 螺旋矩阵 ↑

题目

给你一个 m 行 n 列的矩阵 matrix,请按照顺时针螺旋顺序,返回矩阵中的所有元素。

测试样例

测试样例示意图如下(输入矩阵与螺旋输出顺序):

螺旋矩阵测试样例

提示: m == matrix.length,n == matrix[i].length,1 ≤ m, n ≤ 10,-100 ≤ matrix[i][j] ≤ 100。

代码(Python)解法一:方向数组 + 已访问标记

class Solution(object):
    def spiralOrder(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        if not matrix or not matrix[0]:
            return []

        # 方向: 右, 下, 左, 上
        directions = [[0, 1], [1, 0], [0, -1], [-1, 0]]

        rows = len(matrix)
        columns = len(matrix[0])

        # 创建 visited 矩阵
        visited = [[False] * columns for _ in range(rows)]

        total = rows * columns
        order = [0] * total

        row, col = 0, 0
        direction_index = 0

        for i in range(total):
            order[i] = matrix[row][col]
            visited[row][col] = True

            # 计算下一个位置
            next_row = row + directions[direction_index][0]
            next_col = col + directions[direction_index][1]

            # 如果下一个位置越界或已访问,改变方向
            if (next_row < 0 or next_row >= rows or
                next_col < 0 or next_col >= columns or
                visited[next_row][next_col]):
                direction_index = (direction_index + 1) % 4

            # 更新位置
            row += directions[direction_index][0]
            col += directions[direction_index][1]

        return order

思路解析(解法一)

  • 思路:从左上角 (0,0) 出发,按「右 → 下 → 左 → 上」循环走。用 directions 存四个方向的 (行增量, 列增量),用 visited 标记已访问格子。每次把当前格子加入结果,再计算下一格:若下一格越界或已访问则 direction_index 加 1 取模 4 换方向,然后按当前方向走到下一格。
  • 复杂度:时间复杂度 O(mn),空间复杂度 O(mn)(visited 与结果数组)。思路直观,适合作为第一种实现。

代码(Python)解法二:按层收缩边界

class Solution(object):
    def spiralOrder(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        if not matrix or not matrix[0]:
            return []

        rows = len(matrix)
        columns = len(matrix[0])
        order = []

        left, right = 0, columns - 1
        top, bottom = 0, rows - 1

        while left <= right and top <= bottom:
            # 从左到右遍历上边界
            for col in range(left, right + 1):
                order.append(matrix[top][col])

            # 从上到下遍历右边界
            for row in range(top + 1, bottom + 1):
                order.append(matrix[row][right])

            # 如果还有内圈需要遍历
            if left < right and top < bottom:
                # 从右到左遍历下边界(不包括两端)
                for col in range(right - 1, left, -1):
                    order.append(matrix[bottom][col])

                # 从下到上遍历左边界(不包括两端)
                for row in range(bottom, top, -1):
                    order.append(matrix[row][left])

            # 收缩边界
            left += 1
            right -= 1
            top += 1
            bottom -= 1

        return order

思路解析(解法二)

  • 思路:用四个变量 left、right、top、bottom 表示当前「一圈」的边界。每一轮按顺序:先从左到右走完上边界,再从上到下走完右边界;若当前不止一行且不止一列(left < right and top < bottom),则从右到左走下边界(不含右下角)、从下到上走左边界(不含左上角)。然后边界内缩(left、top 加 1,right、bottom 减 1),重复直到边界交错。
  • 注意:最内层可能只剩一行或一列,此时只走「上→右」两段即可,不再走左下两段,否则会重复,因此用 if left < right and top < bottom 判断。
  • 图解:解法二按层收缩的示意如下。

螺旋矩阵解法二示意

  • 复杂度:时间复杂度 O(mn),空间复杂度 O(1)(不计结果数组),无需 visited,代码更简洁。

LeetCode 48. 旋转图像 ↑

题目

给定一个 n × n 的二维矩阵 matrix 表示一个图像,请你将图像顺时针旋转 90 度。

你必须在原地旋转图像,即直接修改输入的二维矩阵,请不要使用另一个矩阵来旋转图像。本解法一使用额外矩阵便于理解下标关系,与题意不符;满足题意需用原地做法(如解法二)。

测试样例

示意图如下(输入矩阵与顺时针旋转 90° 后的结果):

旋转图像测试样例

提示: n == matrix.length == matrix[i].length,1 ≤ n ≤ 20,-1000 ≤ matrix[i][j] ≤ 1000。

代码(Python)解法一:额外矩阵(与题意不符,仅作理解)

class Solution(object):
    def rotate(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: None Do not return anything, modify matrix in-place instead.
        """
        matrix_new = [[0] * len(matrix[0]) for _ in range(len(matrix))]

        n = len(matrix)

        for i in range(len(matrix)):
            for j in range(len(matrix[0])):
                matrix_new[j][n - i - 1] = matrix[i][j]

        matrix[:] = matrix_new

思路解析(解法一)

  • 下标关系:顺时针旋转 90° 后,原位置 (i, j) 的元素在新矩阵中位于 (j, n - 1 - i),故有 matrix_new[j][n - i - 1] = matrix[i][j]。
  • 流程:先按原矩阵大小建全 0 的 matrix_new,双重循环把每个元素搬到旋转后的位置,最后用 matrix[:] = matrix_new 写回,使调用者看到的是旋转后的矩阵。
  • 与题意不符:题目要求原地、不得使用另一个矩阵,本解法使用了 O(n²) 额外空间,仅作理解用;满足题意见解法二。

代码(Python)解法二:原地四格旋转(符合题意)

class Solution(object):
    def rotate(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: None Do not return anything, modify matrix in-place instead.
        """
        n = len(matrix)

        # 只需要遍历左上角的四分之一矩阵
        for i in range(n // 2):
            for j in range((n + 1) // 2):
                # 四个元素循环交换
                temp = matrix[i][j]
                matrix[i][j] = matrix[n - j - 1][i]
                matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1]
                matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1]
                matrix[j][n - i - 1] = temp

思路解析(解法二)

  • 思路:顺时针 90° 相当于每个「四元组」循环移位:位置 (i, j) → (n-j-1, i) → (n-i-1, n-j-1) → (j, n-i-1) → (i, j)。只需遍历左上角四分之一区域(i in [0, n//2),j in [0, (n+1)//2)),对每个 (i, j) 用临时变量 temp 做上述四个位置的循环交换,即可完成整矩阵原地旋转。
  • 为何只遍历四分之一:每个四元组包含四个对称位置,遍历左上角四分之一时,每个四元组恰好被处理一次,不会重复也不会遗漏。
  • 图解:四格旋转示意如下。

旋转图像解法二示意

  • 复杂度:时间 O(n²),空间 O(1),满足原地、不另开矩阵的题意。

LeetCode 240. 搜索二维矩阵 II ↑

题目

编写一个高效的算法来搜索 m × n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:

  • 每行的元素从左到右升序排列;
  • 每列的元素从上到下升序排列。

提示

  • m == matrix.length
  • n == matrix[i].length
  • 1 ≤ n, m ≤ 300
  • -10⁹ ≤ matrix[i][j] ≤ 10⁹
  • -10⁹ ≤ target ≤ 10⁹

示意图如下:

搜索二维矩阵 II 题意示意

代码(Python)解法一:直接暴力扫描矩阵

class Solution(object):
    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        for row in matrix:
            for element in row:
                if element == target:
                    return True
        return False

思路解析(解法一)

  • 思路:不利用矩阵「行列有序」的性质,直接对每一行、每一个元素做一次遍历比较,找到等于 target 的元素就返回 True,遍历结束仍未找到则返回 False。
  • 复杂度:时间复杂度 O(mn),空间复杂度 O(1)。实现最简单,但没有用上有序信息,是一个「baseline」版本。

代码(Python)解法二:逐行二分查找

class Solution(object):
    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        for row in matrix:
            # 对每一行进行二分查找
            left, right = 0, len(row) - 1
            while left <= right:
                mid = (left + right) // 2
                if row[mid] == target:
                    return True
                elif row[mid] < target:
                    left = mid + 1
                else:
                    right = mid - 1
        return False

思路解析(解法二)

  • 利用有序性:题目保证每一行是升序的,因此可以对每一行独立使用二分查找,将「在一行中找元素」的复杂度从 O(n) 降到 O(log n)。
  • 整体复杂度:共有 m 行,每行一次二分查找,时间复杂度 O(m log n),空间复杂度 O(1)。比完全暴力更高效,但仍然没有充分利用「每列也有序」这一信息(可以作为进一步优化前的一步)。

代码(Python)解法三:从右上角开始逐步缩小(Z 字形搜索)

class Solution(object):
    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        if not matrix or not matrix[0]:
            return False
        
        m, n = len(matrix), len(matrix[0])
        
        # 从右上角开始
        row, col = 0, n - 1
        
        while row < m and col >= 0:
            if matrix[row][col] == target:
                return True
            elif matrix[row][col] > target:
                # 当前值大于目标,向左移动(列减1)
                col -= 1
            else:
                # 当前值小于目标,向下移动(行加1)
                row += 1
        
        return False

思路解析(解法三)

  • 充分利用行列有序:从右上角出发,当前值若等于 target 则找到;若大于 target,说明当前列下方都更大,只能向左(col -= 1);若小于 target,说明当前行左侧都更小,只能向下(row += 1)。每次比较都能排除一行或一列,形成「Z 字形」搜索路径。
  • 复杂度:最多走 m + n 步(行从 0 到 m-1、列从 n-1 到 0),时间复杂度 O(m + n),空间 O(1),是本题最优的线性做法。
posted @ 2026-10-11 15:27  Phier  阅读(10)  评论(0)    收藏  举报