题目链接:LeetCode 48
题目内容:
给定一个 n × n 的二维矩阵表示一个图像。将图像顺时针旋转 90 度。
说明:
你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。
示例 1:
给定 matrix = [ [1,2,3], [4,5,6], [7,8,9] ], 原地旋转输入矩阵,使其变为: [ [7,4,1], [8,5,2], [9,6,3] ]
示例 2:
给定 matrix = [ [ 5, 1, 9,11], [ 2, 4, 8,10], [13, 3, 6, 7], [15,14,12,16] ], 原地旋转输入矩阵,使其变为: [ [15,13, 2, 5], [14, 3, 4, 1], [12, 6, 8, 9], [16, 7,10,11] ]
思路:
<方法一>
其实这道题并不难,题目也很好懂,就是一道“抠边界”的题。这种题,只要看清题目,耐心地去一点点尝试、找出正确的边界,就可以得到答案。这种题的确是没什么太好的解决办法:
此外,这道题提到了“原地”,也就是说不让使用辅助数组。那么我们就不得不玩“击鼓传花”的游戏。因为这个“矩阵”是方阵,所以矩阵的变换涉及到4条边上4个数字的辗转移位。例如a→b→c→d→a这个样子。那么问题就来了,如果直接将d位置的数赋给a,那么a位置的数就会被抹掉,无法赋值到b位置。因而我们需要一个tmp变量,记录一下a位置的数字信息。(其实和最简答的两数交换没区别,然而对于Python而言,我们甚至可以直接写成a,b,c,d = b,c,d,a这种形式;这样当然可以,只不过看上去代码太长不美观,我就分开写了)
上代码
class Solution(object):
def rotate(self, matrix):
"""
:type matrix: List[List[int]]
:rtype: None Do not return anything, modify matrix in-place instead.
"""
for i in range(len(matrix)//2):
m = len(matrix[0])
for j in range(m-1-2*i):
tmp = matrix[i][i+j]
matrix[i][i+j] = matrix[m-1-i-j][i]
matrix[m-1-i-j][i] = matrix[m-i-1][m-1-i-j]
matrix[m-i-1][m-1-i-j] = matrix[i+j][m-i-1]
matrix[i+j][m-i-1] = tmp
简单解释一下,上面外层for循环,控制一共发生几轮方阵正方形的边的互换(4*4方阵2轮互换,3*3方阵1轮互换,中间剩下的那一个数字不需要变换了)。内层for循环,控制着每条边上第几个数字发生变换。内层for循环里面,就是纯粹的抠边界了,自己好好钻研一下吧!
<方法二>
既然涉及到了Python这么强大的语言,我们也该使用一些Python强大的函数库。先复习一下Python3的zip()函数。
我们可以使用zip()函数帮我们做到矩阵转置的操作:
a = [[1,2,3,4],[5,6,7,8]]
b = zip(*a) # zip函数是将对应元素打包成一个个tuple,而zip(*)则可以理解为解压缩生成一个迭代器
list(b)
>>> [(1, 5), (2, 6), (3, 7), (4, 8)] # 将内部的tuple再使用map函数全部转成list,就完成转置了
list(map(list,zip(*a))) # map()内部的list函数将tuple转成list,外层list函数将迭代器迭代出来
>>> [[1, 5], [2, 6], [3, 7], [4, 8]]
而我们可以怎样通过转置操作得到被旋转后的方阵呢?答案是“先上下颠倒矩阵,再做转置操作”。大家可以试试。
简版代码如下:
class Solution(object):
def rotate(self, matrix):
matrix[:] = map(list,zip(*matrix[::-1]))
注意,若写成
matrix = list(map(list,zip(*matrix[::-1])))
就不对了。因为这样可能是不算“原地”了,毕竟我们使用了list函数生成了一个新列表,然后赋值给了变量matrix