Leetcode Python超琐碎笔记: 832. Flipping an Image

超琐碎 ≈ 完整记录 + 深入探究 + 任性吐槽

问题地址,难度:Easy,标签:Array

若有错误之处请予以指正:)

问题描述

Given a binary matrix A, we want to flip the image horizontally, then invert it, and return the resulting image.

To flip an image horizontally means that each row of the image is reversed. For example, flipping [1, 1, 0] horizontally results in [0, 1, 1].

To invert an image means that each 0 is replaced by 1, and each 1 is replaced by 0. For example, inverting [0, 1, 1] results in [1, 0, 0].

Example 1:

Input: [[1,1,0],[1,0,1],[0,0,0]]
Output: [[1,0,0],[0,1,0],[1,1,1]]
Explanation: First reverse each row: [[0,1,1],[1,0,1],[0,0,0]].
Then, invert the image: [[1,0,0],[0,1,0],[1,1,1]]

Example 2:

Input: [[1,1,0,0],[1,0,0,1],[0,1,1,1],[1,0,1,0]]
Output: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]]
Explanation: First reverse each row: [[0,0,1,1],[1,0,0,1],[1,1,1,0],[0,1,0,1]].
Then invert the image: [[1,1,0,0],[0,1,1,0],[0,0,0,1],[1,0,1,0]]

Notes:
1 <= A.length = A[0].length <= 20
0 <= A[i][j] <= 1

题意分析

这个问题分两步, 数组内容的翻转、数组内每个0/1值的反转,两步先后顺序无影响。反转0和1可选用两个简单函数,y=1-xy=x^1(异或)。第三个点是如何进行整个图片矩阵(二维数组)的遍历。

我的实现及调优过程

方法1:76 ms
class Solution:
    def flipAndInvertImage(self, A):
        """
        :type A: List[List[int]]
        :rtype: List[List[int]]
        """
        A = [row[::-1] for row in A]
        A = [list(map(lambda x: 1-x, row)) for row in A]
        return A

最符合我目前Python编码直觉(Pythonic?)的方法。切片-1步长完成数组翻转,map-lambda把反转函数map到数组中的每一个0-1值。

  • 时间复杂度:NA
  • 空间复杂度:NA
方法2:48 ms
class Solution:
    def flipAndInvertImage(self, A):
        """
        :type A: List[List[int]]
        :rtype: List[List[int]]
        """
        A = [row[::-1] for row in A]
        A = [list(map(lambda x: x^1, row)) for row in A]
        return A

与方法1比较仅仅把0-1反转函数换成了异或运算,看来逻辑运算确实比数值快。

  • 时间复杂度:NA
  • 空间复杂度:NA
方法3:72 ms
class Solution:
    def flipAndInvertImage(self, A):
        """
        :type A: List[List[int]]
        :rtype: List[List[int]]
        """
        for row in A:
            i = 0
            j = len(row) - 1
            while (i <= j):
                row[i], row[j] = row[j]^1, row[i]^1
                i += 1
                j -= 1
        return A

自己实现数组翻转、0-1反转和遍历。对于A中的每一行数组,生成头尾两个指针,将头尾指向的值先做0-1反转,再进行交换;将指针不断向中间移动,完成整个翻转。方法3方法1快一点,比方法2慢很多。

  • 时间复杂度:O(m*n) (m为矩阵行数,n为列数)
  • 空间复杂度:O(m)
方法4:48 ms
class Solution:
    def flipAndInvertImage(self, A):
        """
        :type A: List[List[int]]
        :rtype: List[List[int]]
        """
        for row in A:
            for i in range((len(row)+1)//2): # for both even/odd row length
                row[i], row[~i] = row[~i]^1, row[i]^1
        return A

参考Leetcode上的解法,可以理解为方法3的一个小改进,使用了row[~i]来获得row[-i-1] = row[len(row) - 1 - i]~是逻辑运算NOT~(int) = -(int)-1),这样少用了一个指针。此外要配合这种方法,还需要确认交换的循环中,i在列数(每行的长度)为奇数和偶数的情况下都能拿到正确的取值范围。如果i超出正确范围,会把本来已正确交换的位置再错误地交换。我在这里使用了(数组长度+1)//2的方法,这样在两种情况下:

  • 列数为奇数,以3为例:(3+1)//2 = 2range后得到[0,1]
  • 列数为偶数,以4为例:(4+1)//2 = 2range后得到[0,1]

如果列数为奇数,i取到range的最大值时,最中间的值自己和自己交换(row[i]row[~i]指向同一位置),冗余计算了,但是从复杂度上考虑无影响,并且真要优化也比较麻烦,需要先确认列数为奇数,还要确认i已取到range的最大值。

速度上与最快的方法2平齐。

  • 时间复杂度:O(m*n) (m为矩阵行数,n为列数)
  • 空间复杂度:O(m)
方法5:70 ms
class Solution:
    def flipAndInvertImage(self, image: List[List[int]]) -> List[List[int]]:
        _image = []
        for row in image:
            _image.append([1 - row[-(i+1)] for i in list(range(len(row)))])
        return _image

2022重刷,思路跟3差不多,还可以进一步压缩下行数

  • 时间复杂度:O(m*n) (m为矩阵行数,n为列数)
  • 空间复杂度:O(m)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 100天习理财接近尾声,其实后期坚持一点不好,但是根据成功日记的原理,还是要总结一下自己的收获才行,告诉...
    尊的小世界阅读 1,241评论 0 0
  • 我是在算命先生的眼中感情艰难的那一类人。 而事实上,好像也是。 太过透明,太过诚实,反而不容易被珍惜。这是我在感情...
    Janesweety静阅读 1,807评论 0 1
  • 我吧,是那种人,一部剧就会莫名其妙的入坑无法自拔,在坑里待多久我也不知道,但是我饭的偶像每次深入的了解,都有很棒的...
    鱿鱼鱼喵阅读 1,781评论 0 0