主题
复盘 · LC 54 螺旋矩阵
题目
- 题号:LeetCode 54
- 名称:螺旋矩阵(Spiral Matrix)
- 难度:Medium
- 链接:leetcode.cn/problems/spiral-matrix
- 今日代码:
54-螺旋矩阵/index.js
题意
按顺时针螺旋顺序返回矩阵中的所有元素:右 → 下 → 左 → 上,一圈圈往里收。
- 矩阵可以是
m × n(不必方形)。 - 返回一维数组,长度 =
m * n。
边界:单行、单列、1×1、最内层只剩一行或一列。
涉及算法
| 标签 | 一句话 |
|---|---|
| 模拟 | 维护方向与边界(或「已访问」),走到头就转向 |
| 按层剥离 | 用 top/bottom/left/right 收缩四条边 |
教程对照:二维遍历手感接 04 · 数组基础题手感;转向逻辑和 BFS「一层一层」同类,见 11 · 队列与层序思想。
评价我的解法
独立 AC(27/27),但耗时很长(注释里约 2 天),提交约 1ms、仅约 5%。说明最终能对,过程绕了很远。
做法:visitX / visitY 沿线走,用哨兵 10**6 标记已访问,再递归 startVisit 换方向;单行/单列单独特判。
我的代码(摘自 54-螺旋矩阵/index.js;辅助函数很长,这里贴编排入口 + 递归核,完整见源文件):
javascript
function startVisit(matrix, XOrY, direction, x, y) {
const noVisit = 10 ** 6;
let resArr = [];
let newArr = [];
if (XOrY === "x") {
let res = visitX(matrix, y, x, direction, noVisit);
resArr = res.arr;
to = res.to;
if (resArr.length !== 0) {
newArr = startVisit(
matrix,
OtherXY(XOrY),
nextDirection(XOrY, direction),
to,
y,
);
}
} else {
let res = visitY(matrix, x, y, direction, noVisit);
resArr = res.arr;
to = res.to;
newArr = [];
if (resArr.length !== 0) {
newArr = startVisit(
matrix,
OtherXY(XOrY),
nextDirection(XOrY, direction),
x,
to,
);
}
}
resArr.push(...newArr);
return resArr;
}
var spiralOrder = function (matrix) {
if (matrix.length === 1) {
return matrix[0];
}
if (matrix[0].length === 1) {
return matrix.map((arr) => arr[0]);
}
const startItem = matrix[0][0];
const res = startVisit(matrix, "x", 1, 0, 0);
res.unshift(startItem);
return res;
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
问题很实在:
to未声明:写成隐式全局,严格模式会挂;面试也是减分项。- 破坏输入:把格子改成
10**6。本题值域是[-100,100],碰巧安全;换题就炸。应用visited矩阵,或更好——用边界收缩,根本不用脏标记。 - 复杂度意识弱:特判单行/单列,说明通用路径不好控;
nextDirection/OtherXY把简单「右下左上」拆碎,难维护。 - 结构过重:螺旋本质是一个方向数组 + 四边界,不必两套 visit + 递归拼接。
小结:能 AC,但属于「硬模拟到通」;应收成边界收缩模板,默写不超过 30 行。
最佳题解
四边界收缩(不改矩阵):
javascript
/**
* @param {number[][]} matrix
* @return {number[]}
*/
var spiralOrder = function (matrix) {
const res = [];
if (!matrix.length) return res;
let top = 0;
let bottom = matrix.length - 1;
let left = 0;
let right = matrix[0].length - 1;
while (top <= bottom && left <= right) {
for (let j = left; j <= right; j++) res.push(matrix[top][j]);
top++;
for (let i = top; i <= bottom; i++) res.push(matrix[i][right]);
right--;
if (top <= bottom) {
for (let j = right; j >= left; j--) res.push(matrix[bottom][j]);
bottom--;
}
if (left <= right) {
for (let i = bottom; i >= top; i--) res.push(matrix[i][left]);
left++;
}
}
return res;
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- 时间 O(mn),空间 O(1) 额外(不计答案数组)。
- 为何更优:不改原矩阵、无哨兵、无递归;
top<=bottom/left<=right自然处理只剩一行/一列。
关联题目
| 题 | 为何相关 |
|---|---|
| 59. 螺旋矩阵 II | 同一走法,改成「填数」 |
| 48. 旋转图像 | 同日矩阵坐标变换 |
| 498. 对角线遍历 | 另一种矩阵走法模拟 |
一句话带走
螺旋矩阵:维护 top/bottom/left/right,一圈圈收;别靠改格子当 visited。
