Python常见矩阵问题:如何简单获取矩阵元素的上下左右邻边元素——以僵尸矩阵为例

文章介绍了如何解决LeetCode上的僵尸矩阵问题,通过遍历矩阵,判断人类元素周围是否存在僵尸,一旦发现则人类变僵尸,同时处理矩阵边缘情况。使用双重循环和条件判断,当矩阵状态改变时,递归计算直至所有人类变为僵尸,得出所需天数。
摘要由CSDN通过智能技术生成

最近在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循环来判断可以有效使程序短小易懂,减少繁杂。如有更好的思路或者方法欢迎评论区留言,晚生可以好好学习,多谢您的过目。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值