【Leetcode】54. Spiral Matrix -- 我想到了逆时针动 matrix 而不动指针,我知道 zip 可以转置矩阵,但是我却没有写出 one-liner

我想到了逆时针动 matrix 而不动指针,我知道 zip 可以转置矩阵,但是我却没有写出 one-liner 😣


leetcode#54. Spiral Matrix

【描述】:
Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order.

Example 1:

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

Example 2:

Input:
[
  [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]

【分析】:

如果使用数组下标(指针)硬淦,那这道题就是很费时的 medium 题。

但是如果能够发现螺旋遍历二维数组的规律,这道题其实和 Easy 程度相差无几。

规律便是我们不动数组下标(不使用下标螺旋遍历),而通过逆时针翻转 90 二维数组然后读取第一行,以此来实现很简单遍历第一行。

逆时针翻转 90 度,遍历过程如下:

    |1 2 3|      |6 9|      |8 7|      |4|  =>  |5|  =>  ||
    |4 5 6|  =>  |5 8|  =>  |5 4|  =>  |5|
    |7 8 9|      |4 7|
    
      (1)         (2)       (3)        (4)

先遍历第一行;然后不使用下标的方式向下遍历。
而是对除了已经遍历过的第一行外的,剩下的二维数组,逆时针旋转 90 度,这样旋转过后的数组(即 (2)),第一行就是原先要向下遍历的一排。
遍历完 [6,9] 之后,要遍历 [8, 7] 显然重复上面逆时针旋转 90 度过程,再遍历第一行即可。

【Solution】:

该解法依赖逆时针旋转 90 度矩阵的辅助函数,完整代码实现如下:

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if len(matrix) == 0 or len(matrix[0]) == 0: return []

        ans = [i for i in matrix[0]]
        nexT_matrix = rotate_antclockwise(matrix[1:])
        while nexT_matrix:
            for i in nexT_matrix[0]:
                ans.append(i)
            nexT_matrix = rotate_antclockwise(nexT_matrix[1:])
        return ans


def rotate_antclockwise(matrix):
    if not matrix: return []

    dst = []
    for i in range(len(matrix[0])-1, 0-1, -1):  # col: <-<-
        dst.append([matrix[j][i] for j in range(len(matrix))])
    return dst

上面代码细节很丰富,我想经过分析之后,再看代码没有什么不好理解的。
一次 submission 即过,也没有什么问题:

n/a


One-Liner

实际上上面代码可以进一步简化,可以省去很多临时变量之类的东西。

不过首先从逆时针旋转矩阵 90° 开始。

在说逆时针旋转矩阵 90° 之前,我们先看看 one-liner 转置矩阵1

如果 zip(*array) 不经过 x,y,z=zip(*array); 拆分成 x,y,z 三个变量,那么 [[1,4,7],[2,5,8],[3,6,9]];zip(*array) 之后的结果恰好是 [(1, 2, 3), (4, 5, 6), (7, 8, 9)],刚好形成一个转置的关系。这对于所有 array=[[1,2,3],[4,5,6],[7,8,9]]; 的二维数组都是一样的。

img

当然 [(1, 2, 3), (4, 5, 6), (7, 8, 9)] 还不是我们需要的最后的结果。

因为只是一个存放 tuple 的 list,我们要保持原来 list 是存 list 的一致性,所以要应用到上方的 map 函数。

因此对于一个数组的转置,代码如下:

>>> array = [[1, 4], [2, 5], [3, 6]];
>>> map(list, zip(*array));
[[1, 2, 3], [4, 5, 6]]

zip 返回的是惰性迭代的 <tuple> 类型。
如果最终结构需要 二维 list 类型,就需要 map(list, <zip>) 转换。
如果转置只是中间过程,那么没有 map(list, <zip>) 也可以。

从转置矩阵到逆时针旋转90°:

如果仔细观察上面的转置矩阵,就会发现,将转置矩阵按行 reverse,就会得到原矩阵逆时针旋转90°的结果:

[[1, 4, 7],    T    [[1, 2, 3],   reverse  [[7, 8, 9],
 [2, 5, 8],   ==>    [4, 5, 6],     ==>     [4, 5, 6],
 [3, 6, 9]]          [7, 8, 9]]             [1, 2, 3]]

综上,rotate_antclockwise 函数可以写成一行:

def rotate_antclockwise(matrix):
    return list(map(list, zip(*matrix)))[::-1]

当然了,如果不要求返回结果是二维 list 类型,而是中间过程,只要数值是对的即可,那么可以简化写成:

rotated = [*zip(*matrix)][::-1]  # <list <tuple>>

简化逆时针旋转辅助函数

简化过后的 solution 可以写成:

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if len(matrix) == 0 or len(matrix[0]) == 0: return []

        ans = [i for i in matrix[0]]
        nexT_matrix = rotate_antclockwise(matrix[1:])
        while nexT_matrix:
            for i in nexT_matrix[0]:
                ans.append(i)
            nexT_matrix = rotate_antclockwise(nexT_matrix[1:])
        return ans


def rotate_antclockwise(matrix):
    return [*zip(*matrix)][::-1]

递归简化

对于上面 nexT_matrix + while 循环遍历的过程可以简化为递归:

fn: matrix[0] + fn(antclockwise-rotate matrix[1:])
                ||
                \/
                fn: matrix[1] + fn(antclockwise-rotate matrix[2:])
                                ...
                                fn: matrix[N-1] + fn(antclockwise-rotate matrix[N:])
                                                  \/ fn([])  # N = len(matrix)
                                                  fn: return []
class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        return matrix if not matrix else recursive(matrix)

def recursive(matrix):
    return matrix if not matrix else [*matrix.pop(0)] + recursive([*zip(*matrix)][::-1])

one-liner

上面递归函数可以直接使用 spiralOrder 函数,不需要再创建一个 recursive 函数:

def spiralOrder(self, matrix):
    return matrix and [*matrix.pop(0)] + self.spiralOrder([*zip(*matrix)][::-1])

注意 and 在 Python 中代替三元运算表达式的用法。
a and b 不论结果如何,都返回 ab 本身。



Reference


  1. 菜鸟教程 zip 函数 - comment#xxx ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值