Check if a matrix contains only unique connected components - python

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.

Related

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

Leetcode Number of Closed Islands Question (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

minesweeper, reveal empty cell and all other empty cell logic and python

I am currently creating minesweeper, and have got to the part where i have to create a function that once the player clicks on a cell that is empty, it reveals that cell and all other empty cells including the numbered cells that surround the cluster of empty cells ( come on guys, you know minesweeper :) my approach to programming this game, is to have a list of lists which contain values, 0 is empty, 1-8 is how many bombs are touching that cell, and 50 is a bomb. i call this grid. I then use this grid to map out the whole game. That grid is then hidden with a use of another list of lists, which all contain booleans. True is hidden, False is revealed. I call this boolgrid. for example when a player clicks on cell[2][10] the boolgrid[2][10] turns False, revealing that cell.
When an empty cell is selected, the surrounding cells have to be revealed, which in some cases, are also empty, so the surrounding cells of THAT cell need to be revealed, and so on. My problem is writing a function that supports that, i have tried many things, like creating a list of tuples of cell coordinates where the cell == 0, and then a new list to hold all of the new tuples which can eventually be used to turn all of the corresponding boolgrid cells to False. it doesnt work very well, is messy, unpythonic, takes up alot of memory.
I would be most grateful to anyone who can help me with a function that gives me some pythonic way of achieving this.
below is some stripped down code, which contains the bare elements. the code contains all 0's in the grid so every bool should turn False
# used to hold values to denote what the cell contains,
grid = [[0 for x in range(30)] for x in range(15)]
# used to check what cell is hidden, true is hidden, false is revealed,
booleangrid = [[True for x in range(30)] for x in range(15)]
list_to_hold_tuples = []
def find_connected_0_value_cells(cell):
i = 5 # used as an example in the real program these are whatever cell the player selects
j = 10 # as above
# this function should be given an argument of a cell that the player selected.
# reveal the surrounding cells, AND THEN take each surrounding cell, and go through
# the same process again, until every surrounding cell containing 0, and its surrounding
# cells containing zero have been revealed.
# i know i need some kind of loop, but cannot seem to build it.
# currently this function just reveals the cell and its immediate surrounding cells
if cell[i][j] == 0:
s = i, j + 1
t = i, j - 1
u = i - 1, j - 1
v = i - 1, j
w = i - 1, j + 1
x = i + 1, j - 1
y = i + 1, j
z = i + 1, j + 1
tempholding = [s, t, u, v, w, x, y, z]
for i in tempholding:
list_to_hold_tuples.append(i)
def reveal_empty_cells():
for a,b in list_to_hold_tuples:
booleangrid[a][b] = False
print(booleangrid)
find_connected_0_value_cells(grid)
reveal_empty_cells()
I have refactored and got the rest of it to work.
thanks #zettatekdev
grid = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 1, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 2, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 3, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 5, 0, 0, 0, 0, 0, 0],
[1, 1, 2, 5, 0, 0, 0, 0, 0, 0]]
list_to_hold_tuples = []
list_change_boolgrid =[]
row = 6
cell = 10
booleangrid = [[True for x in range(cell)] for x in range(row)]
def find_connected_0_value_cells(a, b):
list_to_hold_tuples.append((a, b))
if grid[a][b] == 0:
coord_list = get_surrounding_coords(a, b)
for a,b in coord_list:
if check_coord_values(a, b):
if grid[a][b] != 0:
c = a,b
if c not in list_to_hold_tuples:
list_to_hold_tuples.append(c)
else:
c = a,b
if c not in list_to_hold_tuples:
find_connected_0_value_cells(a,b)
def add_surrounding_cells():
extra_coord = True
for a,b in list_to_hold_tuples:
if grid[a][b] == 0:
coord_list = get_surrounding_coords(a,b, extra_coord)
for i in coord_list:
if i not in list_change_boolgrid:
list_change_boolgrid.append(i)
else:
c = a,b
if c not in list_change_boolgrid:
list_change_boolgrid.append(c)
def reveal_empty_cells():
global booleangrid
for a, b in list_change_boolgrid:
if check_coord_values(a,b):
booleangrid[a][b] = False
def check_coord_values(a,b):
if a == -1 or a >= row:
return False
if b == -1 or b >= cell:
return False
else:
return True
def get_surrounding_coords(a, b, *extra_coord):
c = (a, b + 1)
d = (a, b - 1)
e = (a - 1, b - 1)
f = (a - 1, b)
g = (a - 1, b + 1)
h = (a + 1, b - 1)
i = (a + 1, b)
j = (a + 1, b + 1)
if extra_coord:
k = (a, b)
return [c, d, e, f, g, h, i, j, k]
return [c, d, e, f, g, h, i, j]
find_connected_0_value_cells(3,5)
add_surrounding_cells()
reveal_empty_cells()
print(booleangrid)
Ok, so I worked with your code for a bit, and I was able to make a code that works. I did adjust the grid size just to make it easier to figure out, but it should work for your original size. Just make sure you don't test with a grid of all 0s like in your posted code because it will return an error for too many recursions. Here's the code.
EDIT: I changed the code so now it reveals the surrounding numbers as well
# used to hold values to denote what the cell contains,
grid = [[0,1,2,5,4,6,0,0,0,0],
[0,1,2,5,4,6,0,0,0,0],
[0,1,2,5,4,6,0,0,5,0],
[0,1,2,5,4,6,0,0,0,0],
[0,1,2,5,4,6,0,0,0,0],
[0,1,2,5,4,6,6,0,0,0]]
# used to check what cell is hidden, true is hidden, false is revealed,
booleangrid = [[True for x in range(10)] for x in range(6)]
list_to_hold_tuples = []
def find_connected_0_value_cells(i,j):
list_to_hold_tuples.append((i,j))
if grid[i][j] == 0:
s = (i, j + 1)
t = (i, j - 1)
u = (i - 1, j - 1)
v = (i - 1, j)
w = (i - 1, j + 1)
x = (i + 1, j - 1)
y = (i + 1, j)
z = (i + 1, j + 1)
tempholding = [s, t, u, v, w, x, y, z]
for a in tempholding:
if a[0]>=0 and a[1]>=0 and a[0]<=len(grid)-1 and a[1]<=len(grid[i])-1 and grid[a[0]][a[1]]==0:
if (a[0]!=i and a[1]!=j) and (a not in list_to_hold_tuples):
find_connected_0_value_cells(a[0],a[1])
list_to_hold_tuples.append(a)
elif a[0]>=0 and a[1]>=0 and a[0]<=len(grid)-1 and a[1]<=len(grid[i])-1:
list_to_hold_tuples.append(a) #this part adds surrounding non-zero numbers
def reveal_empty_cells():
global booleangrid
for a,b in list_to_hold_tuples:
if a>=0 and a<len(booleangrid) and b>=0 and b<len(booleangrid[a]):
booleangrid[a][b] = False
for i in booleangrid:
print(i)
find_connected_0_value_cells(5,8)#Just using coordinates of 5,8 for testing, this would be click spot
reveal_empty_cells()

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

Categories