思路一:模拟
模拟螺旋矩阵的路径,路径超出界限,顺时针旋转,使用一个数组记录当前数字是否被访问到,直到所有的数字全部被访问
class Solution {//一个静态的常量数组,用于标记螺旋矩阵的移动方向(行列变化)//{0, 1}:表示向右移动一格(x 不变,y+1)。//{1, 0}:表示向下移动一格(x+1,y 不变)。//{0, -1}:表示向左移动一格(x 不变,y-1)。//{-1, 0}:表示向上移动一格(x-1,y 不变)static constexpr int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
public:vector<int> spiralOrder(vector<vector<int>>& matrix) {//判断矩阵行列是否为空,如果为空则返回空值if(matrix.size() == 0 || matrix[0].size() == 0){return {};}vector<int> ans;int m = matrix.size();//行最大int n = matrix[0].size();//列最大//数字是否已经被访问的标签数组vector<vector<bool>> visited(m, vector<bool>(n,false));//记录存入答案数组中的个数int col = 0, row = 0;//螺旋矩阵的起始路径//当前方向的索引,可以取0-3,分别对应directions数组中的四个方位int direction_index = 0;for(int count = 0; count < m * n; count++){ans.push_back(matrix[row][col]);//将矩阵中的数字存入结果中visited[row][col] = true;//标记已访问的数字//计算下一位置//行变化列不动,取directions数组中的x坐标//列变化行不动,取directions数组中的y坐标int next_row = row + directions[direction_index][0];int next_col = col + directions[direction_index][1];// 判断是否需要改变移动方向(越界或已访问)if(next_row < 0 || next_row >= m || next_col < 0 || next_col >= n || visited[next_row][next_col]){direction_index = (direction_index + 1) % 4;}//根据判断的方向进行移动row += directions[direction_index][0];col += directions[direction_index][1];}return ans;}
};
constexpr的使用是告诉大家该数组为一个常量表达式,在编译的过程中其值不会改变
这个方法的关键在于写出这个方向数组,我们平时做的题目中如果涉及到处理二维网格上的移动问题,例如:遍历上下左右四个方向;执行 BFS/DFS 等搜索算法;模拟迷宫、棋盘等问题中的移动逻辑,都可以使用这个方向数组
-
时间复杂度:O(mn)
-
空间复杂度:O(mn)
思路二:按层模拟
与上述方法不同的是,这个方法更像是分治的思路,将顺时针矩阵分解成不同层的矩阵,然后又把不同层分解成从左至右、从上至下、从右至左、从下至上四个方向,详解如图所示:
class Solution {
public:vector<int> spiralOrder(vector<vector<int>>& matrix) {//矩阵行或者列为空,说明矩阵为空,则返回空值if(matrix.size() == 0 || matrix[0].size() == 0){return {};}vector<int> ans;int m = matrix.size();//行最大int n = matrix[0].size();//列最大int top = 0, bottom = m - 1;int left = 0, right = n - 1;while(left <= right && top <= bottom){//把每一层的数从左至右放入结果数组中for(int col = left; col <= right; col++){ans.push_back(matrix[top][col]);//将矩阵中的数字存入结果中}//把每一层的数从上至下放入结果数组中for(int row = top + 1; row <= bottom; row++){ans.push_back(matrix[row][right]);//将矩阵中的数字存入结果中}if(left < right && top < bottom){//把每一层的数从右至左放入结果数组中for(int col = right - 1; col > left; col--){ans.push_back(matrix[bottom][col]);//将矩阵中的数字存入结果中}//把每一层的数从下至上放入结果数组中for(int row = bottom; row > top; row--){ans.push_back(matrix[row][left]);//将矩阵中的数字存入结果中}}top++;right--;bottom--;left++;}return ans;}
};
基于上述思想,可以自行选择每一层四个方向的截止位置(也就是每一方向存入数组的最后一个数)和起始位置(也就是每一方向存入数组的第一个数),比如从左至右的截止点可以是[top, right],也可以是[top,right - 1],同时,从上至下的起始点也可以是[top + 1, right],也可以是[top, right]。只要每一方向的起始点和上一方向的截止点保证连续即可
-
时间复杂度:O(mn)
-
空间复杂度:O(1)。该方法与上述方法比,只是使用了四个表示方位的变量,因此空间复杂度为常量级别