剑指offer 29 + LeetCode 54 - 顺时针打印矩阵 - Python

题目描述

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

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

示例 2:

输入: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]

限制:

  • 0 <= matrix.length <= 100
  • 0 <= matrix[i].length <= 100

根据题目的描述可知,这里需要完成的就是顺时针打印矩阵元素的问题。抛开具体的实现不谈,如果要顺时针的打印一个矩阵,示意图如下所示:


剑指offer 29 + LeetCode 54 - 顺时针打印矩阵 - Python_第1张图片

整个打印元素的过程就是访问矩阵元素的过程,根据上图可知,需要如下的辅助设置帮助解题:

  • 方向的控制directions:访问过程所涉及的方向一共有四种:

    • ➡:行不变,列增
    • ⬇:行增,列不变
    • ⬅ :行不变,列减
    • ⬆:行减,列不变
  • 布尔类型访问矩阵vis:用于判断当前元素是否被访问过,如果已被访问过,值为True;否则为False

对于矩阵中某一个位置来说,可以使用[row, col]的坐标形式表示:

  • 如果是向右访问,那么相当于不断的执行[row + 0, col + 1],直到遇到边界位置;
  • 如果是向下访问,那么相当于执行[row -1, col + 0],直到遇到边界位置;
  • 如果是向左访问,那么相当于执行[row + 0, col - 1],直到遇到边界位置;
  • 如果是向上访问,那么相当于执行[row - 1, col + 0], 直到遇到边界位置;

因此,可以使用[0, 1][1, 0][0, -1] [-1, 0]来执行上述的四个操作。依次访问矩阵元素:

  • 如果当前位置还未被访问过,那么设vis[row][col] = True,然后计算下一个访问位置的rowcol的值
  • 边界判断,如果还没有到达边界,那么无需换方向;否则,需要设置下一个方向。边界的判断依据就是:0 <= row < rows, 0 <= col < cols,并且下一个位置没有被访问过vis[row][col] == False
  • 如果需要换方向,当前访问方向标识 + 1后对4取模,得到下一个访问方向的控制
  • 最后,修改rowcol的值,继续访问
class Solution:
    def spiralOrder(self, matrix):
        if not matrix: return []
      
        rows, cols = len(matrix), len(matrix[0])
        vis = [[False] * cols for _ in range(rows)]
        order = []
        directions = [[0, 1], [1, 0], [0, -1], [-1, 0]]
        row, col, flag = 0, 0, 0

        for i in range(rows * cols):
            order.append(matrix[row][col])
            vis[row][col] = True

            nextRow, nextCol = row + directions[flag][0], col + directions[flag][1]
            if not (0 <= nextRow < rows and 0 <= nextCol < cols and not vis[nextRow][nextCol]):
                flag = (flag + 1) % 4
            row, col = row + directions[flag][0], col + directions[flag][1]
            
        return order

另外一种访问方式就是按圈访问,如下所示:


剑指offer 29 + LeetCode 54 - 顺时针打印矩阵 - Python_第2张图片

这种访问方式最重要的就是访问边界的控制,同样是按照➡⬇⬅⬆的访问。我们可以使用4个while循环来控制访问的过程,只要每一次访问没有达到相应的边界,那么就持续访问。一圈访问结束后,进行下一圈的访问,直到访问完最后一圈,对于终止条件的判断,我们可以通过改变每一圈左上角和右下角的坐标表示,假设左上角为[lr, lc],右下角为[rr, rc],那么访问完一圈后,相应的坐标改变为[lr + 1, lc + 1][rr - 1, rc - 1]。如果lr == rr and lc == rc,则访问结束。

另外,当访问到最里面一圈时,此时可能只有一行或是一列(取决于矩阵的行列数值大小)。因此,访问逻辑中需要对单行和单列做额外的访问控制。

class Solution:
    def spiralOrder(self, matrix):
        
        r = []
        def visit(matrix, lr, lc, rr, rc):
            # 如果只有一行
            if lr == rr:
                for i in range(lc, rc + 1):
                    r.append(matrix[lr][i])
            # 如果只有一列
            elif lc == rc:
                for i in range(lr, rr + 1):
                    r.append(matrix[i][lc])
            else:
                curR, curC= lr, lc
                while curC < rc:
                    r.append(matrix[lr][curC])  # 向右访问
                    curC += 1
                while curR < rr:
                    r.append(matrix[curR][rc])  # 向下访问
                    curR += 1
                while curC > lc:
                    r.append(matrix[rr][curC])  # 向左访问
                    curC -= 1
                while curR > lr:
                    r.append(matrix[curR][lc])  # 向上访问
                    curR -= 1

        if not matrix: return []
        
        rows, cols = len(matrix), len(matrix[0])
        # 左上角
        lr, lc = 0, 0
        # 右下角
        rr, rc = rows - 1, cols - 1

        while lr <= rr and lc <= rc:
            visit(matrix, lr, lc, rr, rc)
            lr, lc = lr + 1, lc + 1
            rr, rc = rr -1, rc - 1
        
        return r

你可能感兴趣的:(剑指offer,Leetcode)