Leetcode Number of Closed Islands Question (Python) - python

I'm trying to find the solution for Leetcode question #1254, finding the number of closed islands.
Given a 2D grid consists of 0s (land) and 1s (water). An island is a maximal 4-directionally connected group of 0s and a closed island is an island totally (all left, top, right, bottom) surrounded by 1s.
The way I want to solve this problem is by using a boolean value that decides whether an island has any corner values.
I understand that passing a boolean value as an argument to a function that manipulates its value does not work since it is a local variable.
However, for some reason, the code that I found from geeksforgeeks uses the same method, but it works while mine keeps giving a wrong result.
The following is my code and the one at the bottom is from geeksforgeeks.
class Solution:
def closedIsland(self, grid: List[List[int]]) -> int:
numOfRow, numOfCol = len(grid), len(grid[0])
if numOfRow == 0 or grid == None: return 0
counter = 0
def dfs(row, col, hasCorner):
if not(0 <= row < numOfRow and 0 <= col < numOfCol):
return
if grid[row][col] == 2 or grid[row][col] == 1:
return
else:
if (row == 0 or row == numOfRow -1 or col == 0 or col == numOfCol - 1):
hasCorner = True
grid[row][col] = 2
if numOfRow > row >= 0 and numOfCol > col >= 0 and grid[row][col]:
dfs(row - 1, col, hasCorner)
dfs(row + 1, col, hasCorner)
dfs(row, col - 1, hasCorner)
dfs(row, col + 1, hasCorner)
for row in range(len(grid)):
for col in range(len(grid[0])):
if grid[row][col] == 0:
hasCorner = False
dfs(row, col, hasCorner)
if (hasCorner == False):
counter += 1
print(grid)
return counter
# Python3 program for the above approach
# DFS Traversal to find the count of
# island surrounded by water
def dfs(matrix, visited, x, y, n, m, hasCornerCell):
# If the land is already visited
# or there is no land or the
# coordinates gone out of matrix
# break function as there
# will be no islands
if (x < 0 or y < 0 or
x >= n or y >= m or
visited[x][y] == True or
matrix[x][y] == 0):
return
if (x == 0 or y == 0 or
x == n - 1 or y == m - 1):
if (matrix[x][y] == 1):
hasCornerCell = True
# Mark land as visited
visited[x][y] = True
# Traverse to all adjacent elements
dfs(matrix, visited, x + 1, y, n, m, hasCornerCell)
dfs(matrix, visited, x, y + 1, n, m, hasCornerCell)
dfs(matrix, visited, x - 1, y, n, m, hasCornerCell)
dfs(matrix, visited, x, y - 1, n, m, hasCornerCell)
# Function that counts the closed island
def countClosedIsland(matrix, n, m):
# Create boolean 2D visited matrix
# to keep track of visited cell
# Initially all elements are
# unvisited.
visited = [[False for i in range(m)]
for j in range(n)]
result = 0
# Mark visited all lands
# that are reachable from edge
for i in range(n):
for j in range(m):
if ((i != 0 and j != 0 and
i != n - 1 and j != m - 1) and
matrix[i][j] == 1 and
visited[i][j] == False):
# Determine if the island is closed
hasCornerCell = False
# hasCornerCell will be updated to
# true while DFS traversal if there
# is a cell with value '1' on the corner
dfs(matrix, visited, i, j,
n, m, hasCornerCell)
# If the island is closed
if (not hasCornerCell):
result = result + 1
# Return the final count
return result
# Driver Code
# Given size of Matrix
N, M = 5, 8
# Given Matrix
matrix = [ [ 0, 0, 0, 0, 0, 0, 0, 1 ],
[ 0, 1, 1, 1, 1, 0, 0, 1 ],
[ 0, 1, 0, 1, 0, 0, 0, 1 ],
[ 0, 1, 1, 1, 1, 0, 1, 0 ],
[ 0, 0, 0, 0, 0, 0, 0, 1 ] ]
# Function Call
print(countClosedIsland(matrix, N, M))
# This code is contributed by divyeshrabadiya07
Please help me find what I'm missing here

Related

Check if a matrix contains only unique connected components

I am trying to write a program in python that will return true or false based on if a rectangular matrix contains only unique connected components. I have written a depth first search that checks all adjacent points to build out the connected components. But when I run it on larger data sets I get a stack overflow or hit the RecursionError: maximum recursion depth exceeded in comparison error.
For example give:
AAAABB
AAAABB
CCCCCC
CCCCAC
This would return True since there are two connected components containing the letter A. In the upper left corner and the individual A in the bottom right.
Here is my function:
def check_for_unique_components(matrix, n_cols, n_rows):
visited = np.zeros((n_rows, n_cols))
def valid_node(M, row, col, c, n, l):
return ((row >= 0 and row < n) and (col >= 0 and col < l) and (M[row][col] == c and not visited[row][col]))
def dfs(M, row, col, c, n, l):
rowMoves = [-1, 0, 1, -1, 1, -1, 0, 1]
colMoves = [-1, -1, -1, 0, 0, 1, 1, 1]
visited[row][col] = True
for k in range(8):
if (valid_node(M, row+rowMoves[k], col + colMoves[k], c, n, l)):
dfs(M, row+rowMoves[k], col+colMoves[k], c, n, l)
def connectedComponenets(M):
visited_blocks = set()
n = len(M)
l = len(M[0])
for i in range(n):
for j in range(l):
if (not visited[i][j]):
c = M[i][j]
dfs(M, i, j, c, n, l)
if c in visited_blocks:
return True
else:
visited_blocks.add(c)
return False
return(connectedComponenets(cell_matrix))
The function runs an adjacency search for each value in the matrix and stores each letter that has already been seen in a connected component.

recursive finding longest path of same element in 2d list

I need to find the longest path of 0's in a 2d matrix recursively and can't figure out how to do it.( from a given (i , j) it can only move up, down, right or left)
For example this matrix:
mat = [[1, 0, 0, 3, 0],
[0, 0, 2, 3, 0],
[2, 0, 0, 2, 0],
[0, 1, 2, 3, 3]]
print(question3_b(mat))
This should return 6 as there are communities of sizes 1,3,6.
My attempt: I created a few wrapper functions to find the maximum in a list, and a function to find the route at a given (i,j) element and add it to a list, and doing this on every point(i,j) in the matrix.
def question3_b(mat) -> int:
rows: int = len(mat)
cols: int = len(mat[0])
community_lengths = list()
for i in range(rows):
for j in range(cols):
visited = zeros(rows, cols) # create a 2d list of 0's with size rows,cols
a = findcommunity(mat, (i, j), visited)
print("a=", a)
community_lengths.append(a)
print(community_lengths)
return findmax(community_lengths)
def findcommunity(mat: list, indices: tuple, visited: list): # find length of community
#global rec_counter
#rec_counter += 1
i, j = indices
rows = len(mat)
cols = len(mat[0])
if mat[i][j] == 0:
visited[i][j] = 1
if i < rows - 1:
if visited[i + 1][j] == 0:
return 1 + findcommunity(mat, (i + 1, j), visited)
if j < cols - 1:
if visited[i][j + 1] == 0:
return 1 + findcommunity(mat, (i, j + 1), visited)
if i > 0:
if visited[i - 1][j] == 0:
return 1 + findcommunity(mat, (i - 1, j), visited)
if j > 0:
if visited[i][j - 1] == 0:
return 1 + findcommunity(mat, (i, j - 1), visited)
else: # mat[i][j]!=0
return 0
def zeros(rows:int,cols:int)->list: #create a 2d matrix of zeros with size (rows,cols)
if rows==1 and cols==1:return [[0]]
if rows==1 and cols>1:return [[0]*cols]
if rows>1 and cols>1:return zeros(rows-1,cols)+zeros(1,cols)
def findmax(arr:list)->int: # find max in an array using recursion
if len(arr)==2:
if arr[0]>arr[1]:return arr[0]
else:return arr[1]
else:
if arr[0]>arr[1]:
arr[1]=arr[0]
return findmax(arr[1:])
else:
return findmax(arr[1:])
Where did I go wrong? for a matrix of 4X4 zeros, I expect it to run 16*16 times[16 times for each i,j, and there are 16 elements in the matrix]. but it only runs once.
zeros is a recursive function I made that functions like np.zeros, it creates a 2d matrix full of 0's with specified size.
It got really messy but I tried to just change your code instead of writing a new solution. You should have a look at collections deque. Saw this several times where people keep track of visited a lot easier.
I changed visited to outside of the loop and defined it with np.zeros (didn't have your function ;) ). I'm not sure if your recursive function calls at return were wrong but your if-statements were, or at least the logic behind it (or I didn't understand, also possible :) )
I changed that block completly. The first time you come across a 0 in mat the recursive part will dig into the mat as long as it finds another 0 left,right,bottom or top of it (that's the functionality behind dc and dr). That's where the community_counter is increasing. If the function is returning the last time and you jump out to the outerloop in question_3b the counter gets resetted and searchs for the next 0 (next start of another community).
import numpy as np
def question3_b(mat) -> int:
rows: int = len(mat)
cols: int = len(mat[0])
community_lengths = list()
visited = np.zeros((rows, cols)) # create a 2d list of 0's with size rows,cols
community_count = 0
for row in range(rows):
for col in range(cols):
if visited[row][col]==0:
community_count,visited = findcommunity(mat, (row, col), visited, community_count)
if community_count!=0:
community_lengths.append(community_count)
community_count=0
return community_lengths
def findcommunity(mat: list, indices: tuple, visited: list,community_count: int): # find length of community
i, j = indices
rows = len(mat)
cols = len(mat[0])
visited[i][j] = 1
if mat[i][j] == 0:
community_count += 1
dr = [-1, 0, 1, 0]
dc = [0,-1, 0, 1]
for k in range(4):
rr = i + dr[k]
cc = j + dc[k]
if 0<=rr<rows and 0<=cc<cols:
if visited[rr][cc]==0 and mat[rr][cc]==0:
community_count, visited = findcommunity(mat, (rr,cc), visited, community_count)
return community_count,visited
else:
return community_count,visited
mat = [[1, 0, 0, 3, 0],
[0, 0, 2, 3, 0],
[2, 0, 0, 2, 0],
[0, 1, 2, 3, 3]]
all_communities = question3_b(mat)
print(all_communities)
# [6, 3, 1]
print(max(all_communities))
# 6
EDIT
Here is the findcommunity function in your way. Tested it and it works aswell.
def findcommunity(mat: list, indices: tuple, visited: list,community_count: int): # find length of community
i, j = indices
rows = len(mat)
cols = len(mat[0])
visited[i][j] = 1
if mat[i][j] == 0:
community_count += 1
if i < rows - 1:
if visited[i + 1][j] == 0:
community_count, visited = findcommunity(mat, (i + 1, j), visited, community_count)
if j < cols - 1:
if visited[i][j + 1] == 0:
community_count, visited = findcommunity(mat, (i, j + 1), visited, community_count)
if i > 0:
if visited[i - 1][j] == 0:
community_count, visited = findcommunity(mat, (i - 1, j), visited, community_count)
if j > 0:
if visited[i][j - 1] == 0:
community_count, visited = findcommunity(mat, (i, j - 1), visited, community_count)
return community_count,visited
else:
return community_count,visited
Here a completely different approach, in case someone is interested.
import numpy as np
mat = [[1, 0, 0, 3, 0],
[0, 0, 2, 3, 0],
[2, 0, 0, 2, 0],
[0, 1, 2, 3, 3]]
mat = np.array(mat)
# some padding of -1 to prevent index error
mask = np.full(np.array(mat.shape) + 2, -1)
mask[1:-1, 1:-1 ] = mat
# visiteds are set to -2
def trace(f, pt):
mask[tuple(pt)], pts = -2, [pt - 1]
pts += [trace(f, pt + d) for d in
([0, 1], [1, 0], [0, -1], [-1, 0]) if
mask[tuple(pt + d)] == f]
return pts
clusters = lambda f: {tuple(pt-1): trace(f, pt) for pt in np.argwhere(mask==f) if mask[tuple(pt)]==f}
# now call with value your looking for
print(clusters(0))

How do I write O(n2) program of matrix n x n?

I am practicing and trying to write O(n^2) program that tests whether there are two 1s lying on the same row or the same column in A. Where A = n x n matrix of 0s and 1s.
Given A as:
I should get answer return of 2 matches.
One is on the 1st row, and another on the 3rd column.
My 2nd Attempt:
def testLines():
count = 0
for x in range( 0, len(A)-1 ):
if( (A[x] == 1) & (A[x+1] == 1) ):
count+=1
for y in range( 0, len(A)-1):
if( (A[y] == 1 & A[y+1]) == 1 ):
count+=1
print( count, '1s has been matched in the array A')
testLines()
You want to nest the two loops and change the indexes so that both x and y are parsed. Currently your code moves through (all x, y = 0) and (x = 0, all y).
A = [[0, 0, 1, 1],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 1, 0]]
def testLines():
count = 0
N = len(A)
for x in range(N):
for y in range(N):
if A[x][y] == 1:
if x+1 < N and A[x+1][y] == 1:
count += 1
if y+1 < N and A[x][y+1] == 1:
count += 1
print(count, '1s has been matched in the array A')
testLines()
Alternatively, you can go the Schwarzenegger way and not check if (x+1, y) or (x, y+1) even exist. That will raise IndexErrors that you can choose to ignore.
def testLines():
count = 0
N = len(A)
for x in range(N):
for y in range(N):
try:
if A[x][y] == 1 and A[x+1][y] == 1 or A[x][y+1] == 1:
count += 1
except IndexError:
continue
print(count, '1s has been matched in the array A')
You can run one nested loop (n²) to get summation of rows. If summation is 2 then that row has two 1s.
Now interchange rows and columns(consider rows as columns & vice versa).
Again run nested loop (n²) to check summation of columns.
n²+n²= O(n²)

Programming Maze Solution recursively

This function is intended to recursively navigate a maze and find the length of the shortest path. The path itself is not necessary, only the length. The maze is represented by a 2d list with values such as
0 1 0 0 0
0 0 0 1 0
0 0 0 1 0
The user starts at (0,0) and must end up at the end of the maze as defined (in my case it is the bottom right cell). 1's represent walls.
def maze(x,y,array,length):
m = len(array)
n = len(array[0])
if x < 0 or y < 0 or x == m or y == n or array[x][y] == 1:
return float("inf")
elif x == m - 1 and y == n - 1:
return length
else:
array[x][y] = 1
up = maze(x - 1,y,array,length + 1)
right = maze(x,y + 1,array,length + 1)
down = maze(x + 1,y,array,length + 1)
left = maze(x,y - 1,array,length + 1)
return min(up,down,left,right)
array = [[0,1,0,0,0],[0,0,0,1,0],[0,0,0,1,0]]
minLength = maze(0,0,array,1)
print(minLength)
I designed it so that it recursively finds all possible paths from each direction (up, down, left and right), and returns the lowest value from all these paths with each step of the way. It returns inf for any path that is not valid.
For this specific array, it returns 11, which is false, it should be 9. I do not believe it is merely a mathematical error, as I tried printing each step of the way and it is not recognizing certain paths (it returns inf for paths that most definitely have options).
I can't seem to find where my code is going wrong, it seems like it should properly return the value, but in practice it does not.
array is a reference to the original array, not a local copy. See any of the on-line tutorials on how Python passes function arguments, or how it handles lists. You can see the effect by printing array in your main program after the call to maze:
Final Maze [
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 0]
]
Fixing this is relatively easy: copy the nested list and use that copy locally.
from copy import deepcopy
def maze(x,y,array,length):
m = len(array)
n = len(array[0])
if x < 0 or y < 0 or x == m or y == n or array[x][y] == 1:
return float("inf")
elif x == m - 1 and y == n - 1:
return length
else:
new_maze = deepcopy(array)
new_maze[x][y] = 1
up = maze(x - 1,y,new_maze,length + 1)
right = maze(x,y + 1,new_maze,length + 1)
down = maze(x + 1,y,new_maze,length + 1)
left = maze(x,y - 1,new_maze,length + 1)
return min(up,down,left,right)
array = [[0,1,0,0,0],[0,0,0,1,0],[0,0,0,1,0]]
minLength = maze(0,0,array,1)
print("Final Maze", array)
print(minLength)
The output from this is (edited for readability again)
Final Maze [
[0, 1, 0, 0, 0],
[0, 0, 0, 1, 0],
[0, 0, 0, 1, 0]
]
9

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.

Categories