描述

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

算法

图片说明
我们的目标是按照图中的数字大小顺序访问每个位置的值,然后按顺序保存每个点。

方法一

我们定义一个从 (0, 0) 出发的点,当遇到边界或者访问过的点之后就向右转,已经访问过的点就把它的值赋为一个不可能的值 inf。
如下图,我们访问到边界情况时就向右转
图片说明
如下图,当我们访问到 12 的下一个点的时候就向右转
图片说明
代码实现如下(python版代码注释参照c++版):

// c++
class Solution {
public:
    const int inf = 0x3f3f3f3f; // 访问过的值标记为 inf
    int x[4] = {0, 1, 0, -1}, y[4] = {1, 0, -1, 0}; // x, y 分别是行、列两个方向的向右换方向的顺序
    vector<int> printMatrix(vector<vector<int> > mat) {
        vector<int> res;
        int n = mat.size(), m = mat[0].size();
        int a = 0, b = 0, idx = 0;

        // 一共会访问 m * n 次,循环结束之后就代表所有点都访问过了一次
        for (int i = 0; i < m * n; i++) {
            res.push_back(mat[a][b]);
            mat[a][b] = inf; // 把访问过的值改成 inf
            int s = a + x[idx], t = b + y[idx]; // s, t 是下一个访问的值

            // 如果越界或者遇到访问过的值就需要换方向
            if (s < 0 || s >= n || t < 0 || t >= m || mat[s][t] == inf) {
                idx = (idx + 1) % 4; // 按照顺序改变遍历的方向
                s = a + x[idx], t = b + y[idx]; // 修正越界的 s, t 到合法的方向
            }
            a = s, b = t; // s, t 是合法的下一个访问节点
        }
        return res;
    }
};
# python3
class Solution:
    def printMatrix(self, mat):
        dir = [(0, 1), (1, 0), (0, -1), (-1, 0)]
        inf = 0x3f3f3f3f
        i, j, idx = 0, 0, 0
        n, m = len(mat), len(mat[0])
        res = []
        for _ in range(n * m):
            res.append(mat[i][j])
            mat[i][j] = inf
            a, b = i + dir[idx][0], j + dir[idx][1]
            if a < 0 or a >= n or b < 0 or b >= m or mat[a][b] == inf:
                idx = (idx + 1) % 4
                a, b = i + dir[idx][0], j + dir[idx][1]
            i, j = a, b
        return res

因为我们把所有的节点都访问了一次,所以时间复杂度是 O(m * n)

方法二

方法二用python实现比较简单,其他语言不推荐使用。我们可以把原二维数组想象成一个木板,每次拆去四个方向的边框。
原来的样子
图片说明
拆去上方边框
图片说明
拆去右边边框
图片说明
直到二维矩阵消失。注意要判断每次拆去边框之前矩阵是否合法。

# python3
class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, mat):
        res = []
        while mat:
            res += mat[0]
            mat.pop(0)
            if not mat or not mat[0]: break // 判断矩阵是否合法
            for i in range(len(mat)):
                res += [mat[i][-1]]
                mat[i].pop(-1)
            if not mat: break // 判断矩阵是否合法
            res += mat[-1][::-1]
            mat.pop(-1)
            if not mat or not mat[0]: break // 判断矩阵是否合法
            for i in range(len(mat) - 1, -1, -1):
                res += [mat[i][0]]
                mat[i].pop(0)
        return res

即使我们有的时候是直接弹出了整个一维数组,但是 append 实际上是复制了整个数组,所以最终我们的时间复杂度还是 O(m * n)。