破解编码面试,第 6 版,1.7

Cracking the Coding Interview, 6th edition, 1.7

给定一个由 nxn 矩阵表示的图像,其中图像中的每个像素为 4 个字节,编写一个将图像旋转 90 度的方法。 (到位)

我做了一个低效的解决方案,即将每一行复制到一个临时文件,然后执行交换,花费了 o(N) space,下面的实际解决方案更好,但我不明白。

def rotate_matrix(matrix):
    '''rotates a matrix 90 degrees clockwise'''
    n = len(matrix)
    for layer in range(n // 2):
        first, last = layer, n - layer - 1
        for i in range(first, last):
            # save top
            top = matrix[layer][i]

            # left -> top
            matrix[layer][i] = matrix[-i - 1][layer]

            # bottom -> left
            matrix[-i - 1][layer] = matrix[-layer - 1][-i - 1]

            # right -> bottom
            matrix[-layer - 1][-i - 1] = matrix[i][- layer - 1]

            # top -> right
            matrix[i][- layer - 1] = top
    return matrix

上面的代码是Python的解决方案,

我不明白 for 循环,为什么它在 n//2 范围内,从第一层到 n-layer-1。

另外,为什么题目提到4个字节好像是无用的信息。

谢谢!

考虑 n 是奇数还是偶数很重要,因为必须以细微不同的方式解决问题。不过,在任何一种情况下,正方形都可以分解为象限。

n为奇数:

正方形被分解成四个矩形象限。在 double for-loop 的每次迭代中,恰好有 4 个元素被旋转,每个象限一个。永远不会访问中间元素,因为它在旋转中保持在中间。

+-------+---+ 
| x   x | x | 
+---+---+   | 
| x | x | x | 
|   +---+---+ 
| x | x   x | 
+---+-------+

n偶数大小写:

矩阵被分解成 4 个正方形象限。同样,在 double for-loop 的每次迭代中,恰好旋转了 4 个元素,每个象限一个。 在偶数情况下,每个元素都必须移动(即没有完美的中心元素)。

+-------+-------+
| x   x | x   x |
|       |       |
| x   x | x   x |
+-------+-------+
| x   x | x   x |
|       |       |
| x   x | x   x |
+-------+-------+

n // 2n - layer - 1 只是变相的下限和上限功能。 n is odd case 需要 floor 和 ceiling。这些值用于在其中一个象限上设置迭代。然后,对于 一个 象限中存在的许多元素,4 次交换发生在适当的位置(在 top 临时值的帮助下)。

基本上我们通过从矩阵的外边界到内核来旋转元素,顺时针交换项目。

我们以下面的矩阵为例:

1  2  3  4
5  6  7  8
9  10 11 12
13 14 15 16

在第一次迭代中我们关心的是外部元素:

1  2  3  4
5  x  x  8
9  x  x  12
13 14 15 16

我们开始(内循环)顺时针交换所有元素。在我们的示例中,我们将 4(将其存储在临时变量中)替换为 1。之后,我们将 1 替换为 13,依此类推。最后,我们用保存的 4 替换 16。我们继续这样做,直到所有元素都被交换。所以在外循环的第一次迭代之后,我们的数组看起来像这样:

13  9  5 1
14  6  7 2 
15 10 11 3
16 12  8 4

之后我们进入下一层。在我们的例子中,我们现在看看:

x  x  x x
x  6  7 x 
x 10 11 x
x x  x  x

我们再次将所有元素一一交换,如上所示。我们有 floor n/2 次迭代,因为对于不均匀的 n,最后一次迭代给我们留下了一个元素,显然不需要旋转。

这给我们留下了:

13  9 5 1
14 10 6 2 
15 11 7 3
16 12 8 4

对于像 c 这样我们必须手动管理内存的语言,需要 4 个字节。它们的起源很可能是每个像素由 3 种颜色(红色、绿色和蓝色)组成,并且每种颜色由 8 位(0-255)表示。此外,不透明度可能还有另外 8 位。所以我们剩下 32 位 = 4 个字节。