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⁹
示意图如下:

代码(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),是本题最优的线性做法。

浙公网安备 33010602011771号