解题思路
思路一:外循环打印圈数,内循环打印一圈
从左上角顶点为一圈的起点,打印完一圈后顶点变为右下角一个,外循环的条件是:i < raw/2 and i < col/2
。打印一圈的函数实现较为复杂,要考虑各种情况,打印顺序为→↓←↑。因此,要编写合理的边界条件。同时应注意当某个方向已经无需打印时,后面的方向就无需打印了,因此应当及时结束掉。
- 时间复杂度o(m*n),空间复杂度o(1)
class Solution:
def printMatrix(self , matrix: List[List[int]]) -> List[int]:
raw, col, i = len(matrix), len(matrix[0]), 0
res = []
def printCircle(matrix, start):
i, j = start, start
while i <= col - start - 1: # →打印
res.append(matrix[start][i])
i += 1
i, j = i-1, j+1 # →打印完后下面一个作为起点
if j > raw - start - 1: return
while j <= raw - start - 1: # ↓打印
res.append(matrix[j][i])
j += 1
if i == start: return
i, j = i-1, j-1 # ↓打印完后左边一个作为起点
while i >= start: # ←打印
res.append(matrix[j][i])
i -= 1
i, j = i+1, j-1 # ←打印完后上面一个作为起点
while j > start:
res.append(matrix[j][i])
j -= 1
while i < raw/2 and i < col/2:
printCircle(matrix, i)
i += 1
return res
思路二:缩减四周边界
用 left, right, up, down存储边界,每次打印的范围都将由边界限定。一旦边界收缩到0,则代表已经遍历完,可以终止。
- 时间复杂度o(m*n),空间复杂度o(1)
class Solution:
def printMatrix(self , matrix: List[List[int]]) -> List[int]:
row, col = len(matrix), len(matrix[0])
left, right, up, down = 0, col-1, 0, row-1
res = []
while True:
for i in range(left, right+1):
res.append(matrix[up][i])
up += 1
if up > down: break
for i in range(up, down+1):
res.append(matrix[i][right])
right -= 1
if right < left: break
for i in range(right, left-1, -1):
res.append(matrix[down][i])
down -= 1
if down < up: break
for i in range(down, up-1, -1):
res.append(matrix[i][left])
left += 1
if left > right: break
return res
思路三:用visit标记已走过位置,省去复杂的边界判断
类似于走迷宫一样,用visit数组标记走过的位置,这样每次下一步碰到已经走过的标记时就换方向。方向按照右下左上排布。
- 时间复杂度:o(nm),空间复杂度o(nm)
class Solution:
def printMatrix(self , matrix: List[List[int]]) -> List[int]:
if matrix == [[]]: return []
dxl, dyl = [0, 1, 0, -1], [1, 0, -1, 0]
row, col = len(matrix), len(matrix[0])
visit = [[0]*col for _ in range(row)]
x, y, resStep = 0, 0, row*col-1
visit[x][y], res, direct = 1, [matrix[0][0]], 0
while resStep:
dx, dy = dxl[direct%4], dyl[direct%4]
while x+dx <= row-1 and y+dy <= col-1 and not visit[x+dx][y+dy]:
x, y = x+dx, y+dy
visit[x][y], resStep = 1, resStep-1
res.append(matrix[x][y])
direct += 1
return res
思路四:奇思妙想-逆时针旋转矩阵,每次打印并删除一行
每剔除矩阵最上面一行数据,并添加到顺序列表;然后逆时针90°旋转剩下的矩阵,重复之前的剔除操作;这样直到矩阵为空,此时顺序列表的数字就是我们要找的 顺时针打印的数字!这种方法无需考虑复杂的打印边界条件!
- 时间复杂度o(mn),空间复杂度o(mn)
class Solution:
def printMatrix(self , matrix: List[List[int]]) -> List[int]:
res = []
while matrix:
res.extend(matrix.pop(0))
matrix = list(map(list, zip(*matrix)))[::-1] # 注意:一定要加*解包,否则相当于没变
return res
总结
- 顺时针旋转矩阵:
matrix = list(map(list, zip(*matrix[::-1])))
- 逆时针旋转矩阵:
matrix = list(map(list, zip(*matrix)))[::-1]
为什么要加*
号?
*
字符称为解包运算符。当它出现在可iterable对象后面时,它所做的就是将可iterable中的项逐个传递给函数的调用方,相当于把所有操作数都解压了一层。*二维数组------>数组中的每行的迭代
p = [[1,2,3],[4,5,6]]
>>>d=zip(p)
>>>list(d)
[([1, 2, 3],), ([4, 5, 6],)]
>>>d=zip(*p)
>>>list(d)
[(1, 4), (2, 5), (3, 6)]
可以看到如果不加*号,实际上输出的还是原始值。
因为*
号是将列表(或其他iterable)解包,使其每个元素都成为单独的参数。
所以如果没有*
号,你就是在做zip( [[1,2,3],[4,5,6]], )
,zip后只有一个列表,他会进行迭代操作,把这个列表中的每一个迭代元素(即每一行)和元素为0的空列表进行打包。
使用*
号时,数组中的每一行成为单独的元素,相当于使用zip([1,2,3], [4,5,6])
,这样才会对数组中的每一个元素进行打包。
最后,由于打包后的是元组元素,因此我们使用map将其转化为list。
——————————————————————————————————————————
关于python:zip(list)和zip(* list)之间的区别
Python中的zip(), *zip()与zip(*zip(a,b))