Python Recursion Issue (Leetcode 542) - python

I think I misunderstand some important concepts in Python and it is not specific to the Leetcode problem. I greatly appreciate for any help from who knows Python deeply.
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:
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. 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]].
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
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? 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:
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. 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), i.e the preceding DFS paths have added all their points to it.
I found this article explains "Pass By Reference in Python" well - https://realpython.com/python-pass-by-reference/.
I got your test case to pass by always passing down visited.copy(), i.e 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.

First of all I want to point out that DFS, as well as BFS, is mainly used for searching in trees; 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.
Moreover, with DFS you will need to traverse your matrix many times to find the minimum for every 1s and that's very inefficient.
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 1s and 0s using np.where(matrix == 0). 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

Related

An interview question: find the direction of first step

Description of the problem is below:
Given the matrix of M rows and N columns, 0 represents empty space,- 1 represents obstacle, and 1 represents target points (there are multiple target points).
For each empty space, if you want to reach a target point at the shortest distance, please mark the direction of the first step.
If starting up, you should mark the point as 2. if starting down , you should mark the point as 3. if starting left , you should mark the point as 4. if starting right , you should mark the point as 5.
The priority of direction is up, down, left and right from big to small, that is, if you can reach the target point with the shortest distance from a point up or down, you are supposed to start up.
Returns the matrix after marking. 0< m, n< 1000
I tried to wrote solution in python, but always get 'TypeError: 'NoneType' object is not iterable'. Really don't know why. I would really appreciate your help if you can point out the problem!
My basic idea is, for each empty cell, find its nearest target using BFS. then by specifying this empty cell as start and this nearest target as destination, i find out the direction of the first step. Code might not be so concise, thanks for your time and effort!
class Solution:
"""
#param grid: the input matrix
#return: the new matrix
"""
grid = None
def shortestPath(self, grid):
# BFS
self.grid = grid.copy()
m = len(grid)
n = len(grid[0]) if m > 0 else 0
res = [[None] * n for _ in range(m)]
for i in range(m):
for j in range(n):
if grid[i][j] not in [-1, 1]:
tarx, tary, step = self.bfs(i, j)
res[i][j] = self.search(i, j, tarx, tary)
else:
res[i][j] = grid[i][j]
return res
def search(self, i, j, tarx, tary):
dic = {0: 2, 1: 3, 2: 4, 3: 5}
dirs = [[-1, 0], [1, 0], [0, -1], [0, 1]]
min_dist = float('inf')
direction = -1
for idx, d in enumerate(dirs):
x, y = i + d[0], j + d[1]
if x == tarx and y == tary:
return dic[idx]
if self.inside(x, y):
arr = [tarx, tary]
_, __, dist = self.bfs(x, y, arr)
if min_dist > dist:
min_dist = dist
direction = dic[idx]
return direction
def bfs(self, i, j, target = None):
m = len(self.grid)
n = len(self.grid[0]) if m > 0 else 0
visit = [[False] * n for _ in range(m)]
visit[i][j] = True
dirs = [[-1, 0], [1, 0], [0, -1], [0, 1]]
qu = [(i, j, 0)]
while len(qu) > 0:
ti, tj, step = qu[0][0], qu[0][1], qu[0][2]
qu.pop()
for d in dirs:
x, y = ti + d[0], tj + d[1]
if self.inside(x, y) and not visit[x][y]:
if not target:
if self.grid[x][y] == 1:
return x, y, step + 1
else:
tarx, tary = target[0], target[1]
if x == tarx and y == tary:
return x, y, step + 1
visit[x][y] = True
qu.append((x, y, step + 1))
def inside(self, x, y):
if 0 <= x < len(self.grid) and 0 <= y < len(self.grid[0]) and self.grid[x][y] != -1:
return True
return False
if __name__ == '__main__':
testcase = [[1,0,0],[0,0,0]]
ans = Solution().shortestPath(testcase)
print(ans)
bfs does not return a triple from all possible execution paths. If you finish the loop without finding a solution, then you drop out the bottom and return None. This makes your unpacking assignment fail.
Please see debug for your future use; we expect you to do the initial problem diagnosis, not merely dump an untracked program on us.

how can solve this problem with dynamic programming?

Instructions : rat can move just up or right
input:
The first line contains the number of table size n and the number of cheese m.
From the next line, the position x, y of the cheese is given
Output :
The maximum number of cheese to eat
Exemple1:
input : 1 1 1
output : 1 1
Example2:
input :
3 2
1 2
3 1
output : 1
Example 3:
input :
5 5
2 3
3 2
4 3
4 5
5 2
output: 3
how can I solve with python?
I tried
def maxAverageOfPath(table, N):
dp = [[0 for i in range(N)] for j in range(N)]
dp[0][0] = table[0][0]
# Initialize first column of total table(dp) array
for i in range(0, N):
dp[i][0] = 0
for j in range(0, N):
dp[0][j] = 0
for i in range(0, N):
for j in range(0, N):
print(i, j)
if i == N-1 and j == N-1:
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
continue
if i == N-1 :
dp[i][j] = table[i][j + 1]
continue
if j == N-1 :
dp[i][j] = table[i + 1][j]
continue
dp[i][j] = max(table[i + 1][j], table[i][j + 1])
return dp
but failed...
For dynamic programming you want an edge condition(s) and a way to score where you are right now. After that it's more-or-less smart brute force. The smart part comes from memoizing so you don't repeat work.
Here's a basic recursive approach for python that does the following:
Organize table of cheese as a frozen set of tuples. This can be hashed for memoization and you can determine of a location is in the set in constant time.
Creates an edge condition for the end (when both coordinates are N) and an edge condition for when you walk off the map -- that just returns 0.
Uses lru_cache to memoize. You can implement this yourself easily.
from functools import lru_cache
def hasCheese(table, location):
''' Helper function just to improve readability '''
return 1 if location in table else 0
#lru_cache()
def maxC(table, N, location = (0, 0)):
# edge conditions final square and off the grid:
if location[0] == N and location[1] == N:
return hasCheese(table, location)
if any(l > N for l in location):
return 0
# recursion
score_here = hasCheese(table, location)
return max(score_here + maxC(table, N, (location[0] + 1, location[1])),
score_here + maxC(table, N, (location[0], location[1] + 1))
)
t = frozenset([(2, 3), (3, 2), (4, 3), (4, 5), (5, 2)])
N = 5
print(maxC(t, N))
# prints 3
If you want to do this in a top-down manner using a matrix, you need to be very careful that you always have the previous index set. It's easier to make mistakes doing it this way because you need to get the indexes and order just right. When you set it up as two nested increasing loops, that means the next value is always the current cell plus the max of the two cells one unit less — you should always be looking backward in the matrix. It's not clear what you are trying to do when you are looking forward with this:
dp[i][j] = table[i][j + 1]
because j+1 has not been determined yet.
Since the cheese coordinates are 1 indexed, an easy way forward is to make your matrix zero indexed and N+1 in size. Then when you start your for loops at 1 you can always look and at lower index without undershooting the matrix and avoid a lot of the if/else logic. For example:
def hasCheese(table, location):
''' Helper function just to improve readability '''
return 1 if location in table else 0
def maxAverageOfPath(table, N):
# matrix is sized one unit bigger
dp = [[0 for i in range(N+1)] for j in range(N+1)]
# iterate 1-5 inclusive
for i in range(1, N+1):
for j in range(1, N+1):
# because the zeroth row and column are already zero, this works without undershooting the table
dp[i][j] = hasCheese(table, (i, j)) + max(dp[i][j-1], dp[i-1][j])
# solution is in the corner
return dp[N][N]
t = {(2, 3), (3, 2), (4, 3), (4, 5), (5, 2)}
N = 5
print(maxAverageOfPath(t, N)) #3
When you'r done your matrix will look like:
[0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0]
[0, 0, 0, 1, 1, 1]
[0, 0, 1, 1, 1, 1]
[0, 0, 1, 2, 2, 3]
[0, 0, 2, 2, 2, 3]
Your starting point is at (1, 1) starting in the top-right and your answer is the bottom left corner.
At each point you have two options to move further :
array [row] [col+1]
array [row+1] [col]
As we have to find out a path which involves max cheese.
It can be achived by recurring through the array like below to solve the same:
Solution =>
array [i] [j] + Max(Recur(array [i] [j+1]), Recur(array [i+1] [j]));

What's wrong with this DFS solution?

Problem Description:
You want to build a house on an empty land which reaches all buildings in the shortest amount of distance. You can only move up, down, left and right. You are given a 2D grid of values 0, 1 or 2, where:
Each 0 marks an empty land which you can pass by freely.
Each 1 marks a building which you cannot pass through.
Each 2 marks an obstacle which you cannot pass through.
For example, given three buildings at (0,0), (0,4), (2,2), and an obstacle at (0,2):
1 - 0 - 2 - 0 - 1
| | | | |
0 - 0 - 0 - 0 - 0
| | | | |
0 - 0 - 1 - 0 - 0
The point (1,2) is an ideal empty land to build a house, as the total travel distance of 3+3+1=7 is minimal. So return 7.
I solved this problem by BFS way. Then I want to solve it with DFS way but got stuck. Below is my code:
class Solution(object):
def shortestDistance(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
rl, cl = len(grid), len(grid[0])
builds = sum([col for row in grid for col in row if col == 1])
dist, hit = [[0] * cl for _ in range(rl)], [[0] * cl for _ in range(rl)]
def dfs(x, y, step):
'''
Wrong answer, it seems result dist alway keep the longest distance?
'''
if 0 <= x < rl and 0 <= y < cl and not visited[x][y]:
visited[x][y] = True
if grid[x][y] == 0:
dist[x][y] += (step + 1)
hit[x][y] += 1
dfs(x - 1, y, step + 1)
dfs(x + 1, y, step + 1)
dfs(x, y - 1, step + 1)
dfs(x, y + 1, step + 1)
def bfs(x, y):
'''
works properly
'''
visited = [[False] * cl for _ in range(rl)]
queue =[(x, y, 0)]
while queue:
k, m, step = queue.pop(0)
for i, j in ((k - 1, m), (k + 1, m), (k, m - 1), (k, m + 1)):
if 0 <= i < rl and 0 <= j < cl and not visited[i][j]:
visited[i][j] = True
if grid[i][j] == 0:
dist[i][j] += (step + 1)
hit[i][j] += 1
queue.append((i, j, step + 1))
for i in range(rl):
for j in range(cl):
if grid[i][j] == 1:
# bfs(i, j) # this works properly
visited = [[False] * cl for _ in range(rl)]
dfs(i - 1, j, 0)
dfs(i + 1, j, 0)
dfs(i, j - 1, 0)
dfs(i, j + 1, 0)
ret = float('inf')
for i in range(rl):
for j in range(cl):
if grid[i][j] == 0 and hit[i][j] == builds:
ret = min(ret, dist[i][j])
return ret if ret != float('inf') else -1
# expect 7
print Solution().shortestDistance([[1,0,2,0,1],[0,0,0,0,0],[0,0,1,0,0]])
This is a kind of typical graph search problem. and usually could be solved in both DFS and BFS ways. Just can't figure out how to fix it in DFS way?
Simply DFS is not intended to find the shortest path. With some backtracking and cautious marking and unmarking of visted nodes you could use it to find all paths reaching a given point and choose the shortest one but it is unnecessarily complex and way slower than BFS.

Shortest Path on Chessboard , Python

I have NxN chessboard. I have a pawn that can move 1 box up,down,right,left. There is a random exit point. What I want to do is calculate the shortest path from pawn location to exit location.
Pawn is located at random place
Obstacles are located at random place
Exit place is located at random place
I already created adjaceny array for chessboard. I did it with 2D array. For instance, obstacles are 0 where others are 1. It looks like 2D array. R is pawn. O is obstacle. X are ways that robot can go. E is exit point.
E X X O O
X X X X X
X X R O X
X X X X X
X X X X X
Array representation is
1 1 1 0 0
1 1 1 1 1
1 1 1 0 1
1 1 1 1 1
1 1 1 1 1
My question is should I traverse from R to E and create Tree implementation then calculate the minimum path (DFS, BFS) ? If so how should I do it ?. Or is there another method for that ? For instance using graphs ?
You are right, you should use BFS (Breadth-first Search). It is easy to see that DFS (Depth-first Search) would not necessarily return the shortest. However, it will still tell you whether there exists a path between your pawn and the exit.
To apply the BFS algorithm you should think of your problem as a graph without changing the actual representation of the problem:
vertices of the graphs are all tuples (x, y) that are in your 2D list and are not obstacles
two vertices are connected by an edge if they are "neighbors" in the 2D array
Here's a standard implementation of BFS in which we replace the vertices and edges as explained above.
def BFS(board, start):
queue = list()
queue.append(start)
visited = set()
# this keeps track of where did we get to each vertex from
# so that after we find the exit we can get back
parents = dict()
parents[start] = None
while queue:
v = queue[0]
if board[v[0]][v[1]] == 'E':
break
queue = queue[1:] # this is inefficient, an actual queue should be used
visited.add(v)
for u in neighbors(board, v):
if u not in visited:
parents[u] = v
queue.append(u)
# we found the exit, now we have to go through the parents
# up to the start vertex to return the path
path = list()
while v != None:
path.append(v)
v = parents[v]
# the path is in the reversed order so we reverse it
path = reverse(path)
return path
def neighbors(board, v):
diff = [(0, 1), (0, -1), (1, 0), (-1, 0)]
retval = list()
for d in diff:
newr = d[0] + v[0]
newc = d[1] + v[1]
if newr < 0 or newr >= len(board) or newc < 0 or newc >= len(board[0]):
continue
if board[newr][newc] == 'X':
continue
retval.append((newr, newc))
return retval

Find path in a matrix of 0's and 1's

Recently I came across the following interview question?
Ques: You are given a 2-D matrix with M rows and N columns.You are initially positioned at (0,0) which is the top-left cell in the array. You are allowed to move either right or downwards. The array is filled with 1’s and 0’s. A 1 indicates that you can move through that cell, a 0 indicates that you cannot move through that cell. Return the number of paths from top-left cell to bottom-right cell.(i.e. (0,0)to(M-1,N-1)). Since answer can be large thus you have to return ans%(10^9+7).
Can anyone tell me how to approach or any algorithm that might help?
Edit :
I have an approach
1.Start with the top-left cell,initialize count=0
do
2.Check if 1 exists in adjacent right or adjacent down cell.
3.Add the tuple (i,j) in the stack and choose the right path.
4.If we reach the bottom right cell , update count and pop from stack and go to that position (i,j).
while(stack is not empty)
5.Print count
I was wondering if someone has some other approach?
You can model your problem as a Directed Acyclic Graph (DAG), and then you are looking for number of paths from vertex s to vertex t, which is pretty easy to do in DAG using Dynamic Programming (DP).
In here, it will be done with the following pseudo code:
D(0,0) = 1
D(x,y) = 0 if x < 0
D(x,y) = 0 if y < 0
D(x,y) = 0 if matrix[x][y] = 0
D(x,y-1) + D(x-1,y) Otherwise
By applying Dynamic Programming approach on the above, you will get a matrix, where D(x,y) indicates the number of paths from (0,0) to (x,y), and your solution is D(n,m).
Time complexity of this solution is O(n*m)
Implementing this solution is left for you, since it should be fairly easy after understanding how it is done.
Guys after all my efforts finally I am submitting my code, This prints the path positions in matrix starting from (0,0).
#!/usr/bin/env python
rows, cols = 0, 0
def traverse_matrix(A, directions, i, j, visited):
global rows, cols
def can_we_proceed(A, row, col, visited, current):
return (row >=0 and row < rows and col >=0 and col < cols and \
not visited[row][col] and (current == A[row][col]) and A[row][col] == 1)
visited[i][j] = True
for k in range(len(directions)):
if can_we_proceed(A, i+directions[k][0], j+directions[k][1], visited, A[i][j]):
print "({0},{1})".format(i+directions[k][0],j+directions[k][1])
traverse_matrix(A, directions, i+directions[k][0], j+directions[k][1], visited)
def mysolution(a):
global rows, cols
rows, cols = len(a) , len(a[0])
if (rows == 1 or cols == 1):
return 1
directions = [[1, 0], [0, -1], [-1, 0], [0, 1]]
visited = []
for i in range(rows):
l = []
for j in range(cols):
l.append(False)
visited.append(l)
# tup1 = ();
for i in range(rows):
for j in range(cols):
# tup1 = (i,j);
if not visited[i][j]:
traverse_matrix(A, directions, i, j, visited)
# print "Traversed {0};".format(tup1)

Categories