力扣hot100-54.螺旋矩阵-模拟边界控制详解

LeetCode 54. 螺旋矩阵:模拟 + 边界控制详解

1. 这道题属于什么算法思想

这题主要归类为:

text 复制代码
矩阵 / 模拟 / 边界控制

它不需要动态规划、贪心、回溯或图论。

题目的关键是:

text 复制代码
每次按顺时针走完当前矩阵最外面的一圈,
再把未遍历区域的四条边界向内收缩。

因此需要维护:

text 复制代码
top:当前未遍历区域的上边界。
bottom:当前未遍历区域的下边界。
left:当前未遍历区域的左边界。
right:当前未遍历区域的右边界。

2. 这道题到底在问什么

题目要求按照顺时针螺旋顺序,返回矩阵中的所有元素。

例如:

text 复制代码
matrix =
[
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
]

顺时针螺旋顺序是:

text 复制代码
1 -> 2 -> 3 -> 6 -> 9 -> 8 -> 7 -> 4 -> 5

最终返回:

text 复制代码
[1, 2, 3, 6, 9, 8, 7, 4, 5]

不要把它理解成"在矩阵里转弯走路"。更稳定的理解是:

text 复制代码
每次取走当前最外面一圈。

对于 3 x 3 矩阵:

text 复制代码
第一圈:
1  2  3
4     6
7  8  9

第二圈:
5

3. 四条边界如何表示未遍历区域

对于 m 行、n 列矩阵,初始边界是:

text 复制代码
top = 0
bottom = m - 1
left = 0
right = n - 1

例如 3 x 3 矩阵:

text 复制代码
top = 0
bottom = 2
left = 0
right = 2

表示当前未遍历区域是:

text 复制代码
行范围:0 到 2
列范围:0 到 2

每走完一条边,就说明那条边已经完全处理,不应该再访问,因此边界要向内收缩。

4. 每一圈固定按上、右、下、左走

当前区域由:

text 复制代码
top、bottom、left、right

确定。

一圈的顺时针顺序固定为:

text 复制代码
1. 上边:从左到右。
2. 右边:从上到下。
3. 下边:从右到左。
4. 左边:从下到上。

4.1 从左到右走上边

访问:

text 复制代码
matrix[top][left..right]

走完后:

text 复制代码
top++

因为原来的最上面一行已经处理完。

4.2 从上到下走右边

访问:

text 复制代码
matrix[top..bottom][right]

注意此时 top 已经加一,因此不会重复访问右上角。

走完后:

text 复制代码
right--

4.3 从右到左走下边

访问:

text 复制代码
matrix[bottom][right..left]

走完后:

text 复制代码
bottom--

4.4 从下到上走左边

访问:

text 复制代码
matrix[bottom..top][left]

走完后:

text 复制代码
left++

这样一圈结束,未遍历矩形区域缩小一层。

5. 用 3 x 3 矩阵完整推导

text 复制代码
matrix =
[
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
]

初始:

text 复制代码
top = 0
bottom = 2
left = 0
right = 2
result = []

第一圈:走上边

访问:

text 复制代码
matrix[0][0..2] = 1, 2, 3

结果:

text 复制代码
result = [1, 2, 3]

上边处理完成:

text 复制代码
top = 1

第一圈:走右边

访问:

text 复制代码
matrix[1..2][2] = 6, 9

结果:

text 复制代码
result = [1, 2, 3, 6, 9]

右边处理完成:

text 复制代码
right = 1

第一圈:走下边

访问:

text 复制代码
matrix[2][1..0] = 8, 7

结果:

text 复制代码
result = [1, 2, 3, 6, 9, 8, 7]

下边处理完成:

text 复制代码
bottom = 1

第一圈:走左边

访问:

text 复制代码
matrix[1..1][0] = 4

结果:

text 复制代码
result = [1, 2, 3, 6, 9, 8, 7, 4]

左边处理完成:

text 复制代码
left = 1

第一圈结束后:

text 复制代码
top = 1
bottom = 1
left = 1
right = 1

只剩:

text 复制代码
matrix[1][1] = 5

继续下一圈后,最终结果为:

text 复制代码
[1, 2, 3, 6, 9, 8, 7, 4, 5]

6. 为什么下边和左边需要额外判断

最容易出错的是走完上边和右边后,继续走下边、左边时可能发生重复访问。

单行矩阵

text 复制代码
matrix = [[1, 2, 3]]

第一步从左到右已经访问:

text 复制代码
1, 2, 3

随后:

text 复制代码
top = 1
bottom = 0

说明已经没有剩余行。

如果仍然执行"从右到左走下边",会把同一行重复加入结果。

因此走下边前必须判断:

java 复制代码
if (top <= bottom)

单列矩阵

text 复制代码
matrix =
[
  [1],
  [2],
  [3]
]

第二步从上到下已经访问:

text 复制代码
2, 3

之后:

text 复制代码
left = 0
right = -1

说明已经没有剩余列。

如果仍然执行"从下到上走左边",会把同一列重复加入结果。

因此走左边前必须判断:

java 复制代码
if (left <= right)

7. Java 代码完整注释

java 复制代码
import java.util.ArrayList;
import java.util.List;

class Solution {
    public List<Integer> spiralOrder(int[][] matrix) {
        // 保存螺旋遍历结果。
        List<Integer> result = new ArrayList<>();

        int m = matrix.length;
        int n = matrix[0].length;

        // 当前未遍历区域的四条边界。
        int top = 0;
        int bottom = m - 1;
        int left = 0;
        int right = n - 1;

        // 只要未遍历区域仍是一个有效矩形,就继续处理下一圈。
        while (top <= bottom && left <= right) {
            // 1. 从左到右遍历当前区域的上边。
            for (int j = left; j <= right; j++) {
                result.add(matrix[top][j]);
            }

            // 上边处理完成,向下收缩。
            top++;

            // 2. 从上到下遍历当前区域的右边。
            for (int i = top; i <= bottom; i++) {
                result.add(matrix[i][right]);
            }

            // 右边处理完成,向左收缩。
            right--;

            // 3. 从右到左遍历当前区域的下边。
            // 单行矩阵时,上边已经处理过这一行,不能重复访问。
            if (top <= bottom) {
                for (int j = right; j >= left; j--) {
                    result.add(matrix[bottom][j]);
                }

                // 下边处理完成,向上收缩。
                bottom--;
            }

            // 4. 从下到上遍历当前区域的左边。
            // 单列矩阵时,右边已经处理过这一列,不能重复访问。
            if (left <= right) {
                for (int i = bottom; i >= top; i--) {
                    result.add(matrix[i][left]);
                }

                // 左边处理完成,向右收缩。
                left++;
            }
        }

        return result;
    }
}

8. 用长方形矩阵验证

text 复制代码
matrix =
[
  [1, 2, 3, 4],
  [5, 6, 7, 8],
  [9, 10, 11, 12]
]

第一圈:

text 复制代码
上边:1, 2, 3, 4
右边:8, 12
下边:11, 10, 9
左边:5

得到:

text 复制代码
[1, 2, 3, 4, 8, 12, 11, 10, 9, 5]

剩余中间区域:

text 复制代码
[6, 7]

最终结果:

text 复制代码
[1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7]

9. 复杂度分析

矩阵中的每个元素恰好加入 result 一次。

时间复杂度:

text 复制代码
O(m * n)

除了返回结果 result 外,只维护了四个边界变量。

额外空间复杂度:

text 复制代码
O(1)

如果把题目要求返回的 result 也算入空间,则结果本身占用:

text 复制代码
O(m * n)

10. 总结

这题属于:

text 复制代码
矩阵 / 模拟 / 边界控制

每次循环按固定顺序走完当前最外圈:

text 复制代码
上 -> 右 -> 下 -> 左

每走完一条边,就将对应边界向内收缩:

text 复制代码
上边结束:top++
右边结束:right--
下边结束:bottom--
左边结束:left++

最容易遗漏的是:

text 复制代码
走下边前检查 top <= bottom。
走左边前检查 left <= right。

它们用于防止单行、单列或最后只剩一行/一列时重复访问元素。

可以把整题记成一句话:

text 复制代码
按上、右、下、左取走一圈,再向内收缩四条边界。
相关推荐
shehuiyuelaiyuehao8 小时前
算法31,前缀和,可被k整除的子数组
数据结构·python·算法
203号居民10 小时前
LeetCode hot 100 — 141. 环形链表2
算法·leetcode·链表
玖玥拾10 小时前
LeetCode 202 快乐数
算法·leetcode
LuminousCPP10 小时前
数据结构-二叉树(六):BFS层序遍历与完全二叉树判断|复用链式队列 + (N_0=N_2+1) 性质证明
c语言·数据结构·笔记·算法·二叉树·宽度优先
ZhouDevin11 小时前
算法论文/数据集3——CLD(TMLR2025)压缩训练集,仅保留对验证集有益的样本
人工智能·深度学习·算法·计算机视觉
Tim_1011 小时前
【LeetCode】29、两数相除
算法·leetcode·职场和发展
独立开发之道12 小时前
【three.js教程】Three.js 运行时更新:矩阵、几何体、材质怎么改才不卡
javascript·矩阵·材质
船厂电气自动化ai大模型12 小时前
AI大模型与数学/第63课:矩阵定义、矩阵加法、标量乘法(逐级精讲)
数据结构·人工智能·深度学习·线性代数·算法
余额瞒着我当琳12 小时前
算法修炼 chapter 2 双指针进阶、盛最多水的容器、有效三角形的个数、两数之和、三数之和、四数之和
算法
你压到我腿毛了66612 小时前
C语言冒泡算法(Bubble sort)
c语言·数据结构·算法