最近在Leetcode上刷到一道名为“僵尸矩阵”的题目,要求是:给定一个二维网络,每一个格子都有一个值,2代表墙,1代表僵尸,0代表人类。僵尸每天可以将上下左右最接近的人类感染成僵尸,而不能穿墙,求将所有人类感染成僵尸需要的天数。
问题实例如下:
输入:
[[0,1,2,0,0],
[1,0,0,2,1],
[0,1,0,0,0]]
输出:
2
输入:
[[0,0,0],
[0,0,0],
[0,0,1]]
输出:
4
按照题目要求,我们需要对元素进行判断,而判断对象有两种,一种是人类(0),一种是僵尸(1),两个对象周边的元素值变化是不同的。对于人类,只要周边有一个元素是1 ,就可以置换为1 了,但是对于僵尸,周边除了2之外,都可以置换为1 ,因此多出了将1 重复置换为1 的步骤(至少在题目设定的条件下)。基于此我们的判断对象为人类。
接下来是判断人类周遭元素值了,只要周遭元素有一个是1 ,那它就置换为1;假如周遭元素都是,要么是0,要么是2 ,就保持不变。因此我们要获取人类的周遭元素。
在矩阵中,第一行、最后一行、第一列、最后一列是特殊的,他们的元素的周遭元素都是由缺陷的。为了获取他们的周遭元素,我们要对其上下左右的索引进行判断,但是有如下情况:
处于左上角的元素,它没有左元素和上元素;
处于左下角的元素,它没有左元素和下元素;
处于右上角的元素,它没有右元素和上元素;
处于右下角的元素,它没有右元素和下元素;
处于第一列中间的元素没有左元素;处于最后一列中间的元素没有右元素;
处于第一行中间的元素没有上元素;处于最后一行中间的元素没有下元素。
按照这8个条件判断,如果想单单靠if语句来实现,那真的是逆天了!加上处于矩阵中间的元素,,一共有9中情况。写出来的程序即使能实现,可读性也是比较差的。
获取矩阵元素的周遭元素是很常见的需求,对以上情况进行分析,我们可以知道,可以获取的索引范围,都是在0到L-1的(L为列表长度),我们不妨将周遭索引放在列表之中,然后通过for循环判断,如果不在0到L-1之间,就跳过本次循环。此时需要两个列表,一个是Soda1,一个是Soda2,分别用于存储周遭列索引和行索引。
Soda1 = [i+1,i-1]
Soda2 = [j+1,j-1]
在僵尸矩阵题目中,由于人类周边只要有一个1 ,马上被感染,因此设置Soda1和Soda2也方便我们判断行,行没有1 再判断列,如果有1 直接进入下个循环,减少运算量。第一次感染后判断矩阵中是否有0,如果有就递归在此判断求解,没有就直接返回。
在僵尸矩阵的背景下,我在代码中详细注释获取周遭元素的方法,避免了多重if-elif-else判断:
#僵尸矩阵
class Zombies:
def __init__(self,grid): #构造属性
self.grid = grid
def PickDays(self,S):
L1 = len(self.grid)
L2 = len(self.grid[0])
K = self.grid[:]
for i in range(L1):
for j in range(L2):
if self.grid[i][j] == 0: #判断是不是人类
io1,io2 = i+1,i-1 #io、jo用于周遭索引赋值
jo1,jo2 = j+1,j-1
Soda1 = [io1,io2]
Soda2 = [jo1,jo2]
for s in Soda1:
if s >= 0 and s <= L1-1: #如果我这个列周遭满足条件(存在)
if not (self.grid[s][j] == 0 or self.grid[s][j]==2): #那我周边有僵尸吗?
self.grid[i][j] = 1
break #有僵尸了,变成1,直接进入下一个循环
else:
for so in Soda2: #列周遭没有僵尸,那行周遭呢?
if so >= 0 and so <= L2-1: #如果我这个行周遭满足条件(存在)
if not (self.grid[i][so] == 0 or self.grid[i][so]==2): #那我周边有僵尸吗?
self.grid[i][j] = 1
break #行周遭有僵尸,变成1 ,进入下一个循环
#print(self.grid)
if self.grid != K: #如果僵尸矩阵变了,天数加一
S = S+1
for son in self.grid: #如果还有0,递归进入第二天
if 0 in son:
return self.PickDays(S)
return S
if __name__ == '__main__':
grid = [[0,1,2,0,0],
[1,0,0,2,1],
[0,1,0,0,0]]
grid1 = [[0,0,0],
[0,0,0],
[0,0,1]]
D = Zombies(grid)
D1 = Zombies(grid1)
print(grid)
print('天数:',D.PickDays(0))
print(grid1)
print('天数:',D1.PickDays(0))
输出结果:
[[0, 1, 2, 0, 0],
[1, 0, 0, 2, 1],
[0, 1, 0, 0, 0]]
天数:2
[[0, 0, 0],
[0, 0, 0],
[0, 0, 1]]
天数:4
获取周遭元素是很常见的问题,由于矩阵边角元素的限制,获取周遭元素需要经历多次判断,利用共同条件和列表for循环来判断可以有效使程序短小易懂,减少繁杂。如有更好的思路或者方法欢迎评论区留言,晚生可以好好学习,多谢您的过目。