矩阵基础原理与题目说明
文章目录
- 矩阵基础原理与题目说明
-
- [一、 矩阵与网格基础理论](#一、 矩阵与网格基础理论)
-
- [1.1 经典斜线判定公式(N皇后问题基石)](#1.1 经典斜线判定公式(N皇后问题基石))
- [二、 矩阵遍历与模拟](#二、 矩阵遍历与模拟)
-
- [[73. 矩阵置零](https://leetcode.cn/problems/set-matrix-zeroes/)](#73. 矩阵置零)
- [[54. 螺旋矩阵](https://leetcode.cn/problems/spiral-matrix/)](#54. 螺旋矩阵)
- [三、 矩阵变换与推导](#三、 矩阵变换与推导)
-
- [[48. 旋转图像](https://leetcode.cn/problems/rotate-image/)](#48. 旋转图像)
- [四、 矩阵搜索](#四、 矩阵搜索)
-
- [[240. 搜索二维矩阵 II](https://leetcode.cn/problems/search-a-2d-matrix-ii/)](#240. 搜索二维矩阵 II)
- [五、 矩阵上的回溯算法](#五、 矩阵上的回溯算法)
-
- [[51. N 皇后](https://leetcode.cn/problems/n-queens/)](#51. N 皇后)
🔗 查看完整专栏(LeetCode基础算法专栏)
点击阅读:Python 数据结构与语法速查笔记
点击阅读:哈希表基础原理与题目说明
点击阅读:双指针基础原理与题目说明
点击阅读:滑动窗口基础原理与题目说明
点击阅读:队列与单调队列基础原理与题目说明
点击阅读:二分查找基础原理与题目说明
点击阅读:矩阵基础原理与题目说明
特别说明:
本文为个人的 LeetCode 刷题与学习笔记,内容仅供学习与交流使用,禁止转载或用于商业用途。需要强调的是,文中的题目解法不一定是最优解(可能存在时间或空间复杂度的进一步优化空间),主要目的是分享个人的解题思路与逻辑实现,仅供参考。 笔记内容为个人理解与总结,可能存在疏漏或偏差,欢迎读者自行甄别并交流探讨。

一、 矩阵与网格基础理论
矩阵(二维数组)是算法题中非常常见的数据结构。处理矩阵问题时,除了常规的二维遍历,通常还会涉及到方向模拟、数学推导以及回溯等高级技巧。
1.1 经典斜线判定公式(N皇后问题基石)
在矩阵中判定两个坐标是否在同一条斜线上,有两条极其重要的数学推导公式:
- 主对角线(左上 → \rightarrow → 右下方向) :同一条斜线上的每个位置满足 行下标与列下标之差相等 ,即 r o w − c o l = c o n s t row - col = const row−col=const。
- 副对角线(右上 → \rightarrow → 左下方向) :同一条斜线上的每个位置满足 行下标与列下标之和相等 ,即 r o w + c o l = c o n s t row + col = const row+col=const。
二、 矩阵遍历与模拟
这类问题通常要求按照特定的规则访问矩阵元素,重点在于边界控制与状态标记。
73. 矩阵置零
题目描述:
给定一个 m × n m \times n m×n 的矩阵 matrix,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。要求原地修改矩阵。
解题思路:
为了避免在遍历过程中直接修改矩阵导致连锁覆盖错误(即新变成的 0 又去影响其他行列),可以采用哈希集合标记法:
- 第一次遍历矩阵 :使用两个集合
row_hash和col_hash。若matrix[i][j] == 0,则将行索引i加入row_hash,将列索引j加入col_hash。 - 第二次遍历矩阵 :若当前位置所在行
i存在于row_hash中,或 所在列j存在于col_hash中,则将该位置设为 0。
核心代码:
py
from typing import List
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
"""
原地修改矩阵:利用两个 Hash Set 记录需要置零的行和列
"""
row_hash = set()
col_hash = set()
n = len(matrix) # 行数量
m = len(matrix[0]) # 列数量
# 1. 扫描所有的 0,记录行和列
for i in range(n):
for j in range(m):
if matrix[i][j] == 0:
row_hash.add(i)
col_hash.add(j)
# 2. 根据记录的行列,进行置零操作
for i in range(n):
for j in range(m):
if i in row_hash or j in col_hash:
matrix[i][j] = 0
54. 螺旋矩阵
题目描述:
给你一个 m m m 行 n n n 列的矩阵 matrix ,请按照顺时针螺旋顺序,返回矩阵中的所有元素。
解题思路:
核心为矩阵模拟算法,通过方向数组定义"右 → \rightarrow → 下 → \rightarrow → 左 → \rightarrow → 上"的移动规则:
- 先处理空矩阵边界情况,避免索引报错。
- 从 ( 0 , 0 ) (0,0) (0,0) 出发,每一步记录当前元素、标记已访问、更新访问计数。
- 计算下一步位置,若下一步越界或已访问,则切换方向 (利用模 4 运算实现方向循环
(dir_idx + 1) % 4)。 - 按新方向更新位置,直到遍历完所有元素。
核心代码:
py
from typing import List
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
if not matrix or not matrix[0]:
return []
n, m = len(matrix), len(matrix[0])
# 定义旋转方向, 顺时针:右-下-左-上
direction = [[0, 1], [1, 0], [0, -1], [-1, 0]]
# 记录访问状态
visited = [[False] * m for _ in range(n)]
total = m * n
ans = []
row, col = 0, 0
current_visited = 0
dir_idx = 0
while current_visited < total:
# 1. 记录当前元素 + 标记访问 + 计数
ans.append(matrix[row][col])
visited[row][col] = True
current_visited += 1
if current_visited == total:
break
# 2. 计算下一步位置
next_row = row + direction[dir_idx][0]
next_col = col + direction[dir_idx][1]
# 3. 核心边界判断:先判断「越界」,再判断「已访问」
if not (0 <= next_row < n and 0 <= next_col < m) or visited[next_row][next_col]:
dir_idx = (dir_idx + 1) % 4 # 切换方向
# 4. 更新当前位置
row += direction[dir_idx][0]
col += direction[dir_idx][1]
return ans
三、 矩阵变换与推导
48. 旋转图像
题目描述:
给定一个 n × n n \times n n×n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。你必须在原地旋转图像。
解题思路:
通过数学公式推导,利用四点轮换实现原地旋转。
-
旋转坐标推导 :初始位置 ( i , j ) (i, j) (i,j),顺时针旋转 90° 形成四点闭环:
( i , j ) → ( j , n − 1 − i ) → ( n − 1 − i , n − 1 − j ) → ( n − 1 − j , i ) → ( i , j ) (i,j) \rightarrow (j, n-1-i) \rightarrow (n-1-i, n-1-j) \rightarrow (n-1-j, i) \rightarrow (i,j) (i,j)→(j,n−1−i)→(n−1−i,n−1−j)→(n−1−j,i)→(i,j)
-
反向赋值设计 :为了避免值被覆盖,使用一个临时变量
temp保存初始值,然后逆着轮换方向进行赋值。 -
枚举遍历范围:
- 行遍历 i ∈ [ 0 , n / / 2 ) i \in [0, n//2) i∈[0,n//2)(不包含中心行,避免重复处理)。
- 列遍历 j ∈ [ 0 , ( n + 1 ) / / 2 ) j \in [0, (n+1)//2) j∈[0,(n+1)//2)(包含中心列,避免遗漏)。
- 二者乘积刚好覆盖矩阵的 1 / 4 1/4 1/4 元素,无重复、无遗漏。
核心代码:
py
from typing import List
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
n = len(matrix)
# i 管"环层"(n//2 层),j 管"列覆盖"(包含中心列),刚好覆盖 1/4 区域
for i in range(n // 2):
for j in range((n + 1) // 2):
temp = matrix[i][j]
# 反向赋值,避免值被覆盖
matrix[i][j] = matrix[n-1-j][i]
matrix[n-1-j][i] = matrix[n-1-i][n-1-j]
matrix[n-1-i][n-1-j] = matrix[j][n-1-i]
matrix[j][n-1-i] = temp
四、 矩阵搜索
240. 搜索二维矩阵 II
题目描述:
编写一个高效的算法来搜索 m × n m \times n m×n 矩阵 matrix 中的一个目标值 target。该矩阵每行的元素从左到右升序排列,每列的元素从上到下升序排列。
解题思路:
利用矩阵按行有序的特性,采用逐行二分查找法 (注:本题也有右上角起点的 O ( m + n ) O(m+n) O(m+n) 走法,此处分享个人常用的二分查找思路)。
- 优先进行空矩阵判断,避免索引报错。
- 逐行遍历矩阵,对每一行套用标准的「左闭右闭」一维二分查找模板。
- 若在中途找到目标值立即返回
True,遍历结束未找到则返回False。
核心代码:
py
from typing import List
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
"""
个人解题思路分享:逐行进行二分查找
"""
# 空矩阵判断
if not matrix or not matrix[0]:
return False
n = len(matrix) # 行数
m = len(matrix[0]) # 列数
for i in range(n):
left = 0
right = m - 1
while left <= right:
mid = (left + right) // 2
if matrix[i][mid] == target:
return True
elif matrix[i][mid] < target:
left = mid + 1 # 收缩边界:跳到右半区
else:
right = mid - 1 # 收缩边界:跳到左半区
return False
五、 矩阵上的回溯算法
51. N 皇后
题目描述:
按照国际象棋的规则,皇后可以攻击到与之同列、同行、同斜线的棋子。给你一个整数 n n n,返回所有不同的 n n n 皇后问题的解决方案。
解题思路:
采用回溯法,逐行放置皇后(因为一行只能放一个,天然避免了行冲突)。用一维数组 path 记录每一行皇后所在的列位置。
位置合法性校验规则:
- 冲突检查1(列) :
col == used_col。 - 冲突检查2(主对角线) :
row - col == used_row - used_col。 - 冲突检查3(副对角线) :
row + col == used_row + used_col。
若满足条件则记录并递归至下一行,递归返回后进行回溯撤销。最后当所有行都放满后,将 path 转化为棋盘字符串矩阵加入结果集。
核心代码:
py
from typing import List
class Solution:
def solveNQueens(self, n: int) -> List[List[str]]:
res = []
path = [] # 记录 path[row] = 皇后所在的 col
def is_valid(row, col):
for i in range(len(path)):
used_col = path[i]
# 规则1【列冲突】:当前列等于已放列
if col == used_col:
return False
# 规则2【主对角线冲突】:行坐标差 == 列坐标差
if (row - i) == (col - used_col):
return False
# 规则3【副对角线冲突】:行坐标差 == -(列坐标差)
if (row - i) == -(col - used_col):
return False
return True
def backtrack(row):
# 终止条件:放置到了第 n 行,生成棋盘
if row == n:
board = ["." * j + "Q" + "." * (n - j - 1) for j in path]
res.append(board)
return
# 遍历每一列尝试放置皇后
for col in range(n):
if is_valid(row, col):
path.append(col) # 做选择
backtrack(row + 1) # 递归进入下一行
path.pop() # 撤销选择,回溯
backtrack(0)
return res