54. 螺旋矩阵
题目来源
题目分析
给你一个
m
行n
列的矩阵matrix
,请按照顺时针螺旋顺序,返回矩阵中的所有元素。
题目难度
- 难度:中等
题目标签
- 标签:数组、矩阵
题目限制
m == matrix.length
n == matrix[i].length
1 <= m, n <= 10
-100 <= matrix[i][j] <= 100
解题思路
思路:模拟螺旋过程
- 问题定义:
- 需要按照顺时针螺旋顺序遍历矩阵并返回所有元素。
- 核心算法:
- 使用四个变量分别表示矩阵的左、右、上、下边界。
- 通过循环逐步缩小边界,模拟螺旋遍历的过程。
- 关键步骤:
- 遍历矩阵的上边界,并向右移动。
- 遍历矩阵的右边界,并向下移动。
- 如果还有剩余行和列,遍历矩阵的下边界,并向左移动。
- 如果还有剩余行和列,遍历矩阵的左边界,并向上移动。
- 收缩边界,重复上述过程,直到遍历完所有元素。
核心算法步骤
- 初始化边界:
left
、right
、top
、bottom
分别表示矩阵的左、右、上、下边界。
- 遍历并收缩边界:
- While循环,条件为
left <= right && top <= bottom
。 - 遍历上边界,从左到右。
- 遍历右边界,从上到下。
- 如果内层还有空间,遍历下边界,从右到左。
- 如果内层还有空间,遍历左边界,从下到上。
- 收缩边界:
left++
、right--
、top++
、bottom--
。
- While循环,条件为
- 返回结果:
- 将遍历到的元素添加到结果列表中,最后返回该列表。
代码实现
以下是 Java 代码实现:
/**
* 54. 螺旋矩阵
* @param matrix 矩阵
* @return List<Integer> 顺时针螺旋顺序
*/
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> ans = new ArrayList<>();
int m = matrix.length;
int n = matrix[0].length;
int left = 0, right = n - 1, top = 0, bottom = m - 1;
while (left <= right && top <= bottom) {
for (int i = left; i <= right; i++) {
ans.add(matrix[top][i]);
}
for (int i = top + 1; i <= bottom; i++) {
ans.add(matrix[i][right]);
}
if (left < right && top < bottom) {
for (int i = right - 1; i > left; i--) {
ans.add(matrix[bottom][i]);
}
for (int i = bottom; i > top; i--) {
ans.add(matrix[i][left]);
}
}
left++;
right--;
top++;
bottom--;
}
return ans;
}
代码解读
- 边界初始化:设置初始的左、右、上、下边界。
- 遍历过程:通过四个for循环分别遍历矩阵的四个边界,并在每次遍历后收缩边界。
- 边界条件:在内层遍历前检查是否还有剩余的行和列,以避免重复遍历。
性能分析
- 时间复杂度:O(m*n),其中m和n分别是矩阵的行数和列数。每个元素都被访问一次。
- 空间复杂度:O(1),除了用于存储结果的列表外,使用的额外空间是常数级别的。
测试用例
你可以使用以下测试用例来验证代码的正确性:
// 测试用例1
int[][] matrix1 = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println(spiralOrder(matrix1)); // 输出: [1, 2, 3, 6, 9, 8, 7, 4, 5]
// 测试用例2
int[][] matrix2 = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
System.out.println(spiralOrder(matrix2)); // 输出: [1, 2, 3, 4, 8, 12, 11, 10, 9, 5, 6, 7]
// 测试用例3
int[][] matrix3 = {
{1}
};
System.out.println(spiralOrder(matrix3)); // 输出: [1]
// 测试用例4
int[][] matrix4 = {
{1, 2},
{3, 4}
};
System.out.println(spiralOrder(matrix4)); // 输出: [1, 2, 4, 3]
// 测试用例5
int[][] matrix5 = {
{1, 2, 3},
{4, 5, 6}
};
System.out.println(spiralOrder(matrix5)); // 输出: [1, 2, 3, 6, 5, 4]
扩展讨论
其他实现
- 递归方法:虽然递归可以实现螺旋矩阵的遍历,但可能会增加代码的复杂度,并且对于这个问题,迭代方法已经足够清晰和高效。
- 方向数组:使用方向数组来控制遍历的方向,可以简化边界检查的逻辑,但总体思路与上述方法类似。
性能优化
- 空间优化:在上述实现中,我们已经做到了空间复杂度为O(1),除了输出列表外没有使用额外的空间。
- 边界检查:在遍历内层边界时,通过条件判断避免了重复遍历,这是性能优化的一部分。
总结
这道题目考察了对矩阵遍历的理解和实现能力。通过模拟螺旋遍历的过程,我们可以有效地解决这个问题。在实际应用中,类似的矩阵操作可能会用于图像处理、数据转换等领域。掌握这种遍历方式对于解决更复杂的问题具有重要的基础意义。