<think>我们正在处理的是LeetCode54题:螺旋矩阵。给定一个mxn的矩阵,按照螺旋顺序返回矩阵中的所有元素。根据引用内容,我们可以采用模拟螺旋路径的方法,使用方向数组来控制移动方向,当遇到边界或已访问的元素时改变方向。算法步骤:1.定义方向数组DIRS,表示右、下、左、上四个方向(顺时针)。2.初始化一个与输入矩阵同样大小的visited矩阵(或者修改原矩阵标记已访问)?但引用[1]中使用了将访问过的位置标记为Integer.MAX_VALUE的方法,这样可以在原矩阵上操作,避免额外空间。但注意题目要求不能修改原矩阵?题目没有明确,但通常我们应避免修改原数据。不过引用[1]的方法修改了原矩阵,如果要求不修改,则需要额外空间。3.另一种方法是使用边界收缩法(引用[3]提到),但引用[1]的方法更简洁。然而,题目要求返回列表,不要求保留原矩阵,所以我们可以修改原矩阵(但题目没有说可以修改,所以最好不修改?)。但引用[1]的解法修改了原矩阵。我们也可以使用一个visited数组来记录访问过的位置,这样就不修改原矩阵。考虑到题目矩阵大小不超过10x10,使用额外空间是可以接受的。但引用[1]的方法已经比较高效,且代码简洁。这里我们采用引用[1]的方法,但注意题目中矩阵元素可能包含负数,所以标记值不能是Integer.MIN_VALUE?因为题目中元素范围在-100到100,所以我们可以使用一个超出这个范围的值(比如101或Integer.MAX_VALUE)作为标记。但是,题目没有说不能修改原矩阵,所以我们可以使用。但是,如果我们不想修改原矩阵,我们可以使用一个同样大小的boolean数组来记录访问情况。两种方法都可以,这里我们提供两种实现,但根据引用[1],我们先给出修改原矩阵的实现(因为代码简洁),然后再提供一个不修改原矩阵的实现(使用边界收缩法)。不过,引用[3]提到边界收缩法,我们也可以实现。由于问题要求包含输入输出示例,我们将提供:1.算法实现(两种方法)2.输入输出示例方法一:模拟路径(修改原矩阵)-使用方向数组,按顺序向右、向下、向左、向上移动。-当下一步到达矩阵边界或已经访问过的位置时,改变方向(通过取余循环切换方向)。-使用一个变量记录当前方向,每次移动后判断下一步是否需要转向。方法二:边界收缩法(不修改原矩阵)-定义四个边界:上边界top=0,下边界bottom=m-1,左边界left=0,右边界right=n-1。-按照顺时针方向遍历:1.从左到右遍历上边界(top行),遍历完上边界下移(top++)2.从上到下遍历右边界(right列),遍历完右边界左移(right--)3.从右到左遍历下边界(bottom行),遍历完下边界上移(bottom--)4.从下到上遍历左边界(left列),遍历完左边界右移(left++)-循环条件:top<=bottom且left<=right方法二更直观,且不需要修改原矩阵,也不需要额外的标记数组,空间复杂度O(1)(除了输出列表)。我们选择方法二作为主要实现,因为它更符合题目要求(不修改原矩阵)且高效。下面给出Java实现:方法二:边界收缩法步骤:初始化top=0,bottom=m-1,left=0,right=n-1。初始化结果列表ans。while(top<=bottom&&left<=right){1.从左到右:遍历top行的left到right列,将元素加入ans。然后top++。2.从上到下:遍历right列的top到bottom行,将元素加入ans。然后right--。3.检查是否还有行(因为top可能已经超过bottom):从右到左:遍历bottom行的right到left列(逆序),将元素加入ans。然后bottom--。4.检查是否还有列:从下到上:遍历left列的bottom到top行(逆序),将元素加入ans。然后left++。}注意:在第3步和第4步之前,需要检查是否还有行/列,因为可能已经遍历完了(比如只有一行或一列的情况)。具体代码:输入输出示例:输入:matrix=[[1,2,3,4],[5,6,7,8],[9,10,11,12]]输出:[1,2,3,4,8,12,11,10,9,5,6,7]代码实现:</think>### LeetCode 54:螺旋矩阵 Java 实现
#### 问题描述
给定一个 m×n 的矩阵,按照顺时针螺旋顺序返回矩阵中的所有元素。
**示例输入**:
```java
int[][] matrix = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
```
**示例输出**:
```
[1,2,3,4,8,12,11,10,9,5,6,7]
```
---
### 高效实现方案(边界收缩法)
```java
import java.util.ArrayList;
import java.util.List;
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> result = new ArrayList<>();
if (matrix == null || matrix.length == 0) return result;
int top = 0, bottom = matrix.length - 1;
int left = 0, right = matrix[0].length - 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;
}
}
```
---
### 算法解析
1. **边界初始化**:
- `top=0`, `bottom=行数-1`
- `left=0`, `right=列数-1`
2. **四步循环**:
```java
while (top <= bottom && left <= right) {
// 向右遍历 → [top行, left→right]
// 向下遍历 ↓ [right列, top→bottom]
// 向左遍历 ← [bottom行, right→left] (需检查top<=bottom)
// 向上遍历 ↑ [left列, bottom→top] (需检查left<=right)
}
```
3. **边界收缩**:
- 每完成一个方向遍历后收缩对应边界
- 例如:完成顶层遍历后 `top++`
4. **条件检查**:
- 向左/向上遍历前需检查边界有效性
- 防止单行/单列情况下的重复遍历[^1]
---
### 时间复杂度分析
- **时间复杂度**:$O(m \times n)$
需访问矩阵中的每个元素恰好一次
- **空间复杂度**:$O(1)$(不考虑输出列表)
仅使用固定数量的指针变量
---
### 测试用例
```java
public static void main(String[] args) {
Solution solution = new Solution();
// 示例测试
int[][] matrix1 = {{1,2,3},{4,5,6},{7,8,9}};
System.out.println(solution.spiralOrder(matrix1));
// 输出: [1,2,3,6,9,8,7,4,5]
int[][] matrix2 = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
System.out.println(solution.spiralOrder(matrix2));
// 输出: [1,2,3,4,8,12,11,10,9,5,6,7]
// 边界测试
int[][] matrix3 = {{7}};
System.out.println(solution.spiralOrder(matrix3)); // 输出: [7]
}
```
---
### 应用场景
1. **图像处理**:像素矩阵的螺旋遍历
2. **游戏开发**:地图探索路径规划
3. **数据压缩**:矩阵数据的螺旋编码
4. **内存管理**:二维存储结构的访问优化[^3]
---
### 相关问题
1. 如何实现**逆时针**螺旋遍历矩阵?
2. 如何处理**非矩形**(如三角形)矩阵的螺旋遍历?
3. 如何将螺旋遍历算法扩展**三维矩阵**?
4. 在图像处理中,螺旋遍历如何优化**局部性原理**?
5. 如何修改算法实现**从外向内**和**从内向外**两种螺旋顺序?[^2]