Python 递归问题 (Leetcode 542)

时间:2021-01-17 09:04:55

标签: python recursion depth-first-search

我想我误解了 Python 中的一些重要概念,它不是特定于 Leetcode 问题的。我非常感谢深入了解 Python 的人提供的任何帮助。

Leetcode 542 是给定一个仅由 0 和 1 组成的二维数组,对于每个 1,找到到达 0 的最短距离。我有一个虚拟的 DFS 解决方案:

  class Solution:
    def updateMatrix(self, matrix):
     
      dists = []
      for _ in range(len(matrix)):
          dists.append([0]*len(matrix[0]))
    
      for y in range(len(matrix)):
          for x in range(len(matrix[0])):
              if matrix[y][x] == 1:
                  self.min_dist = len(matrix)+len(matrix[0])
                  self.DFS(x, y, matrix, 0, set({}))
                  dists[y][x] = self.min_dist
                
      return dists

    def DFS(self, x, y, matrix, distance, visited):
    
      if (x, y) in visited or (matrix[y][x] == 1 and distance > self.min_dist): return
    
      if matrix[y][x] == 0:
          print (x, y, "d:", distance)
          print ('------')
          self.min_dist = min(self.min_dist, distance)
          return 
    
      print (x, y, distance)
    
      visited.add((x, y))
    
      if x > 0 and (x-1, y) not in visited: 
          self.DFS(x-1, y, matrix, distance+1, visited)
        
      if y > 0 and (x, y-1) not in visited: 
          self.DFS(x, y-1, matrix, distance+1, visited)
   
      if x < len(matrix[0])-1 and (x+1, y) not in visited: 
          self.DFS(x+1, y, matrix, distance+1, visited)
   
      if y < len(matrix)-1 and (x, y+1) not in visited: 
          self.DFS(x, y+1, matrix, distance+1, visited)
   

简单的 DFS 直到达到 0。每次我们调用 DFS 时,distance + 1。对我来说看起来不错。但是测试用例 input = [[1,0,0],[0,1,1],[1,1,1]] 给了我 dist = [[1,0,0],[0,1,1],[1,2,3]]

如果将matrix[y][x] == 1改为matrix[y][x] == 1 and x==2 and y==2并运行上面的代码,输出为

 2 2 0
 1 2 1
 0 2 2
 0 1 d: 3
 ------
 1 1 2
 0 1 d: 3
 ------
 1 0 d: 3
 ------
 2 1 3
 2 0 d: 4
 ------

在 (x,y)= (2,1) 处,初始距离更改为 3。但 (2,1) 处的初始距离应为 1。我的问题是为什么会更改?谁能帮我指出我做错了什么?谢谢!

3 个答案:

答案 0 :(得分:1)

你真的不需要递归。您可以简单地将需要更新其邻居的位置排队,并继续更新/排队位置,直到不再进行更新:

def getDistances(matrix):
    rows,cols = len(matrix),len(matrix[0])
    maxDist   = rows*cols+1 # start 1's at maximum distance
    result    = [[maxDist*bit for bit in row] for row in matrix]
    more      = { (r,c) for r,row in enumerate(matrix)
                        for c,bit in enumerate(row) if bit == 0}
    while more: # process queue starting with zero positions
        r,c     = more.pop()
        newDist = result[r][c]+1 # neighbours are at distance+1 from here
        for nr,nc in [(r,c+1),(r,c-1),(r+1,c),(r-1,c)]: # 4 directions
            if nr not in range(rows): continue
            if nc not in range(cols): continue            
            if newDist >= result[nr][nc]: continue 
            result[nr][nc] = newDist  # reduce neighbour's distance
            more.add((nr,nc))         # queue neighbour to cascade updates
    return result

输出:

m = [[0,0,0,0,0,1],
     [0,1,1,0,0,0],
     [1,1,1,1,0,1],
     [1,1,1,1,1,0]]

for r in getDistances(m): print(r)
                  
[0, 0, 0, 0, 0, 1]
[0, 1, 1, 0, 0, 0]
[1, 2, 2, 1, 0, 1]
[2, 3, 3, 2, 1, 0]  

答案 1 :(得分:1)

一直在看这个。问题似乎是修改 visited 集的方式。我认为它是通过引用传递的,这意味着当它尝试去 (2,2) -> (2,1) 时,集合已经包含点 (2,1),即前面的 DFS 路径已将其所有点添加到其中。

我发现这篇文章很好地解释了“Python 中的引用传递” - https://realpython.com/python-pass-by-reference/

我让你的测试用例总是向下传递visited.copy(),即self.DFS(x-1, y, matrix, distance+1, visited.copy())。我不是 Python 专家,但我想有更简洁的方法来处理这个问题。

答案 2 :(得分:1)

首先我想指出,DFS和BFS一样,主要用于树中搜索;实际上,您可以将您的矩阵视为一棵特定的树,但我不会为此任务走这条路,因为您不需要搜索,而是保持与尊重您所有的邻居、父母和孩子
此外,使用 DFS,您将需要多次遍历矩阵才能找到每个 1 的最小值,这非常低效。

关于你的问题,如果你跟踪你正在创建的堆栈,你会得到:

 2 2 0
 1 2 1
 0 2 2
 0 1 d: 3
 ------
 back to (0, 2), with distance = 2
 (1, 2) already visited
 back to (1, 2) with distance = 1
 1 1 2
 0 1 d: 3
 ------
 back to (1, 1) with distance = 2
 1 0 d: 3
 ------
 back to (1, 1) with distance = 2
 2 1 3
 2 0 d: 4

回到您的任务,因为您使用的是 python,我将使用 numpy 来处理此任务,并使用 1 查找 0np.where(matrix == 0)。那么这只是做一些微积分的问题:

import numpy as np


class Solution:

    def update_matrix(self, matrix):
        matrix = np.array(matrix)

        x_ones, y_ones = np.where(matrix == 1)
        x_zeros, y_zeros = np.where(matrix == 0)

        for i in range(len(x_ones)):
            temp = []

            for j in range(len(x_zeros)):
                temp.append(abs(x_ones[i] - x_zeros[j]) + abs(y_ones[i] - y_zeros[j]))

            matrix[x_ones[i], y_ones[i]] = min(temp)

        return matrix.tolist()

如果您一定不能使用外部库,请按以下步骤操作:

class Solution:

    def update_matrix(self, matrix):
        x_ones, y_ones = [], []
        x_zeros, y_zeros = [], []

        # First scan to save coordinates
        for i in range(len(matrix)):
            for j in range(len(matrix[0])):
                if matrix[i][j] == 1:
                    x_ones.append(i)
                    y_ones.append(j)
                else:
                    x_zeros.append(i)
                    y_zeros.append(j)

        for i in range(len(x_ones)):
            temp = []

            for j in range(len(x_zeros)):
                temp.append(abs(x_ones[i] - x_zeros[j]) + abs(y_ones[i] - y_zeros[j]))

            matrix[x_ones[i]][y_ones[i]] = min(temp)

        return matrix