1. 矩阵置零
题目描述
给定一个 m x n 的整数矩阵,如果某个元素为 0,则将其所在行与列的所有元素都设为 0。要求原地修改。
解题思路
💡 核心技巧:利用首行首列作为标记空间(原地标记)
若直接置零会丢失原始信息,需先标记再统一处理。
关键洞察:
- 使用
matrix[0][*]标记各列是否应置零 - 使用
matrix[*][0]标记各行是否应置零 - 但首行和首列自身也需要被标记 → 额外用两个布尔变量
firstRowZero和firstColZero记录
代码实现
/**
* @param {number[][]} matrix
* @return {void} Do not return anything, modify matrix in-place instead.
*/
var setZeroes = function(matrix) {
const m = matrix.length;
const n = matrix[0].length;
let firstRowZero = false;
let firstColZero = false;
// 检查首行是否有0
for (let j = 0; j < n; j++) {
if (matrix[0][j] === 0) {
firstRowZero = true;
break;
}
}
// 检查首列是否有0
for (let i = 0; i < m; i++) {
if (matrix[i][0] === 0) {
firstColZero = true;
break;
}
}
// 用首行/首列标记内部区域的0
for (let i = 1; i < m; i++) {
for (let j = 1; j < n; j++) {
if (matrix[i][j] === 0) {
matrix[i][0] = 0;
matrix[0][j] = 0;
}
}
}
// 根据标记置零内部区域
for (let i = 1; i < m; i++) {
for (let j = 1; j < n; j++) {
if (matrix[i][0] === 0 || matrix[0][j] === 0) {
matrix[i][j] = 0;
}
}
}
// 处理首行和首列
if (firstRowZero) {
for (let j = 0; j < n; j++) matrix[0][j] = 0;
}
if (firstColZero) {
for (let i = 0; i < m; i++) matrix[i][0] = 0;
}
};
复杂度分析
- 时间复杂度:O(mn)
- 空间复杂度:O(1)
2. 螺旋矩阵
题目描述
给定一个 m x n 矩阵,按顺时针螺旋顺序返回所有元素。
解题思路
💡 核心技巧:边界收缩法(模拟遍历)
维护四个边界:top,bottom,left,right,每走完一圈就收缩边界。
关键洞察:
- 按「上→右→下→左」顺序遍历
- 每次遍历完一条边,对应边界向内收缩
- 注意:在遍历下边和左边前,需检查
top <= bottom和left <= right,防止重复访问
代码实现
/**
* @param {number[][]} matrix
* @return {number[]}
*/
var spiralOrder = function(matrix) {
if (matrix.length === 0 || matrix[0].length === 0) return [];
let top = 0;
let bottom = matrix.length - 1;
let left = 0;
let right = matrix[0].length - 1;
const result = [];
while (top <= bottom && left <= right) {
// 从左到右遍历上边界
for (let col = left; col <= right; col++) {
result.push(matrix[top][col]);
}
top++;
// 从上到下遍历右边界
for (let row = top; row <= bottom; row++) {
result.push(matrix[row][right]);
}
right--;
// 从右到左遍历下边界
if (top <= bottom) {
for (let col = right; col >= left; col--) {
result.push(matrix[bottom][col]);
}
bottom--;
}
// 从下到上遍历左边界
if (left <= right) {
for (let row = bottom; row >= top; row--) {
result.push(matrix[row][left]);
}
left++;
}
}
return result;
};
复杂度分析
- 时间复杂度:O(mn)
- 空间复杂度:O(1)
3. 旋转图像
题目描述
给定一个 n x n 的二维矩阵,原地顺时针旋转 90 度。
解题思路
💡 核心技巧:转置 + 行反转
数学变换:顺时针旋转 90° = 先沿主对角线转置,再水平翻转每行。
关键洞察:
- 转置:
matrix[i][j] ↔ matrix[j][i] - 行反转:
matrix[i].reverse() - 两者结合即为旋转
代码实现
/**
* @param {number[][]} matrix
* @return {void} Do not return anything, modify matrix in-place instead.
*/
var rotate = function(matrix) {
const n = matrix.length;
// Step 1: 转置(沿主对角线翻转)
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
[matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]];
}
}
// Step 2: 每行反转(水平翻转)
for (let i = 0; i < n; i++) {
matrix[i].reverse();
}
};
复杂度分析
- 时间复杂度:O(n²)
- 空间复杂度:O(1)
4. 搜索二维矩阵 II
题目描述
给定一个 m x n 矩阵,每行从左到右升序,每列从上到下升序。判断目标值 target 是否存在。
解题思路
💡 核心技巧:从左下角(或右上角)开始的“阶梯搜索”
- 起点选在左下角(或右上角)是因为这个位置具有“一个方向增大,一个方向减小”的特性
- 每一步都能排除一行或一列,不会遗漏可能包含target的区域
- 因此,算法是贪心且完备的
关键洞察:
- 从左下角
(m-1, 0)出发:- 若
matrix[x][y]=target→找到目标值 - 若
matrix[x][y] < target→ 当前行过小,y++ - 若
matrix[x][y] > target→ 当前列过大,x--
- 若
- 类似二叉搜索树的路径
代码实现
/**
* @param {number[][]} matrix
* @param {number} target
* @return {boolean}
*/
var searchMatrix = function (matrix, target) {
let m = matrix.length
let n = matrix[0].length
let x = m - 1
let y = 0
while (x >= 0 && y < n) {
if (matrix[x][y] === target) return true
else if (matrix[x][y] < target) y++
else x--
}
return false
};
复杂度分析
- 时间复杂度:O(m + n)
- 空间复杂度:O(1)
总结对比
| 题目 | 核心技巧 | 关键优化 |
|---|---|---|
| 矩阵置零 | 原地标记(首行首列作哈希) | 用两个布尔变量保护首行首列 |
| 螺旋矩阵 | 边界收缩模拟 | 遍历下/左边前需检查边界有效性 |
| 旋转图像 | 转置 + 行反转 | 数学变换替代复杂坐标计算 |
| 搜索二维矩阵 II | 阶梯搜索 | 利用行列有序性实现线性搜索 |
希望这篇解析对你有帮助!欢迎继续关注 LeetCode 热题 100 系列 👋