Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[LeetCode] 54. Spiral Matrix #54

Open
grandyang opened this issue May 30, 2019 · 4 comments
Open

[LeetCode] 54. Spiral Matrix #54

grandyang opened this issue May 30, 2019 · 4 comments

Comments

@grandyang
Copy link
Owner

grandyang commented May 30, 2019


请点击下方图片观看讲解视频
Click below image to watch YouTube Video
Video

Given an m x n matrix, return all elements of the matrix in spiral order.

Example 1:

Input: matrix = [[1,2,3],[4,5,6],[7,8,9]]
Output: [1,2,3,6,9,8,7,4,5]

Example 2:

Input: matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]]
Output: [1,2,3,4,8,12,11,10,9,5,6,7]

Constraints:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 10
  • -100 <= matrix[i][j] <= 100

这道题让我们搓一个螺旋丸,将一个矩阵按照螺旋顺序打印出来,只能一条边一条边的打印,首先要从给定的 mxn 的矩阵中算出按螺旋顺序有几个环,注意最中间的环可以是一个数字,也可以是一行或者一列。环数的计算公式是 min(m, n) / 2,知道了环数,就可以对每个环的边按顺序打印,比如对于题目中给的那个例子,个边生成的顺序是(用颜色标记了数字,Github 上可能无法显示颜色,请参见博客园上的帖子) Red -> Green -> Blue -> Yellow -> Black

1 2 3

4 5 6

7 8 9

定义 p,q 为当前环的高度和宽度,当p或者q为1时,表示最后一个环只有一行或者一列,可以跳出循环。此题的难点在于下标的转换,如何正确的转换下标是解此题的关键,可以对照着上面的 3x3 的例子来完成下标的填写,代码如下:

解法一:

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        vector<int> res;
        int c = m > n ? (n + 1) / 2 : (m + 1) / 2;
        int p = m, q = n;
        for (int i = 0; i < c; ++i, p -= 2, q -= 2) {
            for (int col = i; col < i + q; ++col) 
                res.push_back(matrix[i][col]);
            for (int row = i + 1; row < i + p; ++row)
                res.push_back(matrix[row][i + q - 1]);
            if (p == 1 || q == 1) break;
            for (int col = i + q - 2; col >= i; --col)
                res.push_back(matrix[i + p - 1][col]);
            for (int row = i + p - 2; row > i; --row) 
                res.push_back(matrix[row][i]);
        }
        return res;
    }
};

如果觉得上面解法中的下标的转换比较难弄的话,也可以使用下面这种坐标稍稍简洁一些的方法。对于这种螺旋遍历的方法,重要的是要确定上下左右四条边的位置,那么初始化的时候,上边 up 就是0,下边 down 就是 m-1,左边 left 是0,右边 right 是 n-1。然后进行 while 循环,先遍历上边,将所有元素加入结果 res,然后上边下移一位,如果此时上边大于下边,说明此时已经遍历完成了,直接 break。同理对于下边,左边,右边,依次进行相对应的操作,这样就会使得坐标很有规律,并且不易出错,参见代码如下:

解法二:

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        vector<int> res;
        int up = 0, down = m - 1, left = 0, right = n - 1;
        while (true) {
            for (int j = left; j <= right; ++j) res.push_back(matrix[up][j]);
            if (++up > down) break;
            for (int i = up; i <= down; ++i) res.push_back(matrix[i][right]);
            if (--right < left) break;
            for (int j = right; j >= left; --j) res.push_back(matrix[down][j]);
            if (--down < up) break;
            for (int i = down; i >= up; --i) res.push_back(matrix[i][left]);
            if (++left > right) break;
        }
        return res;
    }
};

若对上面解法中的多个变量还是晕的话,也可以使用类似迷宫遍历的方法,这里只要设定正确的遍历策略,还是可以按螺旋的方式走完整个矩阵的,起点就是(0,0)位置,但是方向数组一定要注意,不能随便写,开始时是要往右走,到了边界或者访问过的位置后,就往下,然后往左,再往上,所以 dirs 数组的顺序是 右->下->左->上。还需要一个 visited 数组来记录某个位置是否已经访问过,访问过值为1,否则为0,这样再判断新位置的时候,只要其越界了,或者是 visited 数组中的值为1了,就表明此时需要转弯了,到 dirs 数组中去取转向的 offset,得到新位置,注意这里的 dirs 数组中取是按循环数组的方式来操作,加1然后对4取余,按照这种类似迷宫遍历的方法也可以螺旋遍历矩阵,参见代码如下:

解法三:

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size(), idx = 0, i = 0, j = 0;
        vector<int> res;
        vector<vector<int>> visited(m, vector<int>(n));
        vector<vector<int>> dirs{{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
        for (int k = 0; k < m * n; ++k) {
            res.push_back(matrix[i][j]);
            visited[i][j] = 1;
            int x = i + dirs[idx][0], y = j + dirs[idx][1];
            if (x < 0 || x >= m || y < 0 || y >= n || visited[x][y] == 1) {
                idx = (idx + 1) % 4;
                x = i + dirs[idx][0];
                y = j + dirs[idx][1];
            }
            i = x;
            j = y;
        }
        return res;
    }
};

Github 同步地址:

#54

类似题目:

Spiral Matrix II

Spiral Matrix III

Spiral Matrix IV

参考资料:

https://leetcode.com/problems/spiral-matrix/

https://leetcode.com/problems/spiral-matrix/discuss/20719/0ms-Clear-C%2B%2B-Solution

https://leetcode.com/problems/spiral-matrix/discuss/20599/Super-Simple-and-Easy-to-Understand-Solution

LeetCode All in One 题目讲解汇总(持续更新中...)

(欢迎加入博主的知识星球,博主将及时答疑解惑,并分享刷题经验与总结,快快加入吧~)

知识星球 喜欢请点赞,疼爱请打赏❤️~.~

微信打赏

|

Venmo 打赏


---|---

@butterfly0510
Copy link

谢谢分享!

FYI, 参考你的思路一写的, 不过没有用 int p or int q

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int> > &matrix) {
        if (matrix.empty() || matrix[0].empty()) return {};
        vector<int> res;
        const int M = matrix.size();
        const int N = matrix[0].size();
        int cnt = M > N ? (N + 1) / 2 : (M + 1) / 2;
        for (int i = 0; i < cnt; ++i) {
            for (int col = i; col < N - i; ++col) {
                res.push_back(matrix[i][col]);
            }
            for (int row = i+1; row  < M - i; ++row) {
                res.push_back(matrix[row][N-1-i]);
            }
            if (M > N 
                ? ((N - i) * 2 - 1 == N)
                : ((i + 1) * 2 - 1 == M)) {
                    break;
            }
            for (int col = N-i-2; col >= i; --col) {
                res.push_back(matrix[M-1-i][col]);
            }
            for (int row = M-i-2; row >= i+1; --row) {
                res.push_back(matrix[row][i]);
            }
        }
        return res;
    }
};

@jimbinc
Copy link

jimbinc commented Jul 26, 2020

@jimbinc
Copy link

jimbinc commented Aug 12, 2020

@0liu
Copy link

0liu commented Aug 13, 2020

This problem can be solved by recursively rotating the matrix in counter-clockwise.
Python:

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        return matrix and list(matrix.pop(0)) + self.spiralOrder(list(zip(*matrix))[::-1])

C++

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> res;
        if (matrix.empty())
            return res;
        // Add 1st row to result
        int C = matrix[0].size();
        for (int i = 0; i < C; ++i)
            res.push_back(matrix[0][i]);
        // Copy from 2nd row and rotate
        vector<vector<int>> rotated;
        if (matrix.size() > 1) {
            for (int c = 0; c < C; ++c) {
                rotated.push_back(vector<int>{});
                for (int r = 1; r < matrix.size(); ++r)
                    rotated[c].push_back(matrix[r][C-c-1]);
            }
        }
        // Recursive call
        vector<int> res0 = spiralOrder(rotated);
        // Combine results from recusive call
        if (!res0.empty())
            res.insert(res.end(), res0.begin(), res0.end());
        return res;
    }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants