简体   繁体   English

Python 递归问题(Leetcode 542)

[英]Python Recursion Issue (Leetcode 542)

I think I misunderstand some important concepts in Python and it is not specific to the Leetcode problem.我想我误解了 Python 中的一些重要概念,它并不是特定于 Leetcode 问题。 I greatly appreciate for any help from who knows Python deeply.我非常感谢深入了解 Python 的人提供的任何帮助。

The Leetcode 542 is that given a 2D array consisting of 0 and 1 only, and for each 1, find the shortest distance to reach 0. I have a dummy DFS solution: 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)
   

Simply DFS until reaching 0. Every time we call DFS, distance + 1 .只需 DFS 直到达到 0。每次我们调用 DFS, distance + 1 It looks good to me.对我来说看起来不错。 But a test case input = [[1,0,0],[0,1,1],[1,1,1]] gives me dist = [[1,0,0],[0,1,1],[1,2,3]] .但是一个测试用例input = [[1,0,0],[0,1,1],[1,1,1]]给了我dist = [[1,0,0],[0,1,1],[1,2,3]]

If change matrix[y][x] == 1 to matrix[y][x] == 1 and x==2 and y==2 and run the above code, the output is如果将matrix[y][x] == 1更改为matrix[y][x] == 1 and x==2 and y==2并运行上述代码,则 output 为

 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
 ------

At (x,y)= (2,1) the initial distance is changed to 3. but the initial distance at (2,1) should be 1. My question is why it changes?在 (x,y)= (2,1) 处,初始距离更改为 3。但 (2,1) 处的初始距离应为 1。我的问题是它为什么会改变? Can anyone help me point out where I did wrong?谁能帮我指出我做错了什么? Thanks!谢谢!

You don't really need recursion for this.你真的不需要递归。 You can simply queue positions that need to update their neighbours and keep updating/queuing positions until no more updates are made:您可以简单地将需要更新其邻居的位置排队并继续更新/排队位置,直到不再进行更新:

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

output: output:

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]  

Been taking a look at this.一直在看这个。 It seems the problem is the way the visited set is modified.似乎问题在于修改visited集的方式。 I think it's being passed by reference which means by the time it tries to go (2,2) -> (2,1) the set already contains the point (2,1) , ie the preceding DFS paths have added all their points to it.我认为它是通过引用传递的,这意味着当它尝试 go (2,2) -> (2,1)该集合已经包含点(2,1) ,即前面的 DFS 路径已经添加了所有点给它。

I found this article explains "Pass By Reference in Python" well - https://realpython.com/python-pass-by-reference/ .我发现这篇文章很好地解释了“Python 中的引用传递” - https://realpython.com/python-pass-by-reference/

I got your test case to pass by always passing down visited.copy() , ie self.DFS(x-1, y, matrix, distance+1, visited.copy()) .我通过始终传递visited.copy visited.copy()self.DFS(x-1, y, matrix, distance+1, visited.copy()) )) 使您的测试用例通过。 I'm not a Python expert and imagine there are cleaner ways to handle this though.我不是 Python 专家,但可以想象有更清洁的方法来处理这个问题。

First of all I want to point out that DFS, as well as BFS, is mainly used for searching in trees ;首先我要指出的是,DFS 和 BFS 一样,主要用于树中的搜索 indeed, you can think your matrix as a particular tree, but I wouldn't go that path for this task because you don't need to search but rather to keep track of some distance with respect to all of your neighbors, parents and children .实际上,您可以将您的矩阵视为一棵特定的树,但我不会 go 执行此任务的路径,因为您不需要搜索,而是跟踪与所有邻居、父母和孩子的距离.
Moreover, with DFS you will need to traverse your matrix many times to find the minimum for every 1 s and that's very inefficient.此外,使用 DFS,您将需要多次遍历矩阵以找到每1秒的最小值,这是非常低效的。

Regarding your question, if you keep track of stack you're creating, you will get:关于您的问题,如果您跟踪正在创建的堆栈,您将获得:

 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

Back to your task, since you're using python I would tackle this task by using numpy , and look for 1 s and 0 s using np.where(matrix == 0) .回到您的任务,因为您使用的是 python 我将使用 numpy 来解决此任务,并使用numpy np.where(matrix == 0)查找1 s 和0 s。 Then it's just a matter of doing some calculus:那么这只是做一些微积分的问题:

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()

If you must not use external libraries, just proceed as follows:如果您不能使用外部库,请按照以下步骤操作:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM