Find Maximum Connected Colors (Values) in a 2D Grid using DFS or
- 时间:2020-09-17 10:57:36
- 分类:网络文摘
- 阅读:72 次
Given a 2D Grid with integer values representing the colours, you are asked (in an interview probably) to find out the maximum connected colour sizes. A colour is connected to another if it is horizontally or vertically connecting to another piece in the Grid that has the same value. For example, given the following 2D Grid:
1 2 3 4 5 | grid = [[1, 0, 1, 1], [0, 1, 1, 0], [1, 0, 1, 1], [1, 0, 1, 1] ] |
grid = [[1, 0, 1, 1], [0, 1, 1, 0], [1, 0, 1, 1], [1, 0, 1, 1] ]
The maximum connected colours are the the following (leaving out others) which has size of 8:
1 2 3 4 5 | grid = [[0, 0, 1, 1], [0, 1, 1, 0], [0, 0, 1, 1], [0, 0, 1, 1] ] |
grid = [[0, 0, 1, 1], [0, 1, 1, 0], [0, 0, 1, 1], [0, 0, 1, 1] ]
Let’s define the grid in Python:
1 2 3 | class Grid: def __init__(self, data): self.data = data |
class Grid: def __init__(self, data): self.data = data
And a few helper function return the colour for a pixel and -1 if the coordinate is invalid.
1 2 3 4 5 | def data_at(self, row, col): if row >= 0 and row < len(self.data) and \ col >= 0 and col < len(self.data[row]): return self.data[row][col] return -1 |
def data_at(self, row, col): if row >= 0 and row < len(self.data) and \ col >= 0 and col < len(self.data[row]): return self.data[row][col] return -1
With this, then we can return neighbours which should have valid coordinates and same colour:
1 2 3 4 5 6 7 | def neighbours(self, row, col): P = [[0, -1], [0, 1], [1, 0], [-1, 0]] n = [] for p in P: if self.data[row][col] == self.data_at(row + p[0], col + p[1]): n.append((row + p[0], col + p[1])) return n |
def neighbours(self, row, col): P = [[0, -1], [0, 1], [1, 0], [-1, 0]] n = [] for p in P: if self.data[row][col] == self.data_at(row + p[0], col + p[1]): n.append((row + p[0], col + p[1])) return n
We are using the coordinate offsets here to save a few typings.
Finding Connected Colours using Depth First Search Algorithm
The Depth First Search Algorithm should be conducted for each pixel, thus in the double for loop:
1 2 3 | for row in range(len(self.data)): for col in range(len(self.data[row])): ans = max(ans, self.dfs(row, col, {})) |
for row in range(len(self.data)): for col in range(len(self.data[row])): ans = max(ans, self.dfs(row, col, {}))
The Depth First Search function takes the first two as coordinate parameters, and the third parameter is a dictionary (hash set) which is used to mark the visited pixel – to avoid re-visiting the same pixels over and over again – otherwise the DFS will be endless.
1 2 3 4 5 6 7 8 9 | def dfs(self, row, col, visited): key = str(row) + "," + str(col) if key in visited: return 0 visited[key] = True ans = 1 for n in self.neighbours(row, col): ans += self.dfs(n[0], n[1], visited) return ans |
def dfs(self, row, col, visited): key = str(row) + "," + str(col) if key in visited: return 0 visited[key] = True ans = 1 for n in self.neighbours(row, col): ans += self.dfs(n[0], n[1], visited) return ans
The current position is checked first to ensure that it has never been handled. Then, we add the current pixel to the visited list. Then, for all its neighbours, we call the DFS and accumulate the result number.
The DFS can be implemented in an iterative fashion.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def dfs_iterative(self, row, col): st = [(row, col)] visited = {} ans = 0 while st: cur = st.pop() key = str(cur[0]) + "," + str(cur[1]) if key in visited: continue visited[key] = True ans += 1 for n in self.neighbours(cur[0], cur[1]): st.append((n[0], n[1])) return ans |
def dfs_iterative(self, row, col): st = [(row, col)] visited = {} ans = 0 while st: cur = st.pop() key = str(cur[0]) + "," + str(cur[1]) if key in visited: continue visited[key] = True ans += 1 for n in self.neighbours(cur[0], cur[1]): st.append((n[0], n[1])) return ans
The key to implement a recursion using iterative approach is using a stack (First In Last Out). After the validation of a new node (visited the first time), we increment the counter – as we find one more connected piece.
Finding Connected Colours using Breadth First Search Algorithm
The BFS is similar, except that the order of the neighbour nodes is different. This is achieved via using a queue data structure (First In First Out).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | def bfs(self, row, col): q = [(row, col)] visited = {} ans = 0 while len(q): cur = q.pop(0) key = str(cur[0]) + "," + str(cur[1]) if key in visited: continue visited[key] = True ans += 1 for n in self.neighbours(cur[0], cur[1]): q.append((n[0], n[1])) return ans |
def bfs(self, row, col): q = [(row, col)] visited = {} ans = 0 while len(q): cur = q.pop(0) key = str(cur[0]) + "," + str(cur[1]) if key in visited: continue visited[key] = True ans += 1 for n in self.neighbours(cur[0], cur[1]): q.append((n[0], n[1])) return ans
Breadth First Search Algorithm is often implemented in a iterative approach and in this particular case, no difference (except the usage of stack or queue) to the iterative Depth First Search Algorithm i.e. you can count the number of pixels level by level (BFS) or as deep as possible (DFS).
Demo Code in Python
All above is included in the following Python code to demo the BFS or DFS:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | class Grid: def __init__(self, data): self.data = data def max_connected_grid(self, algorithm): ans = 1 if algorithm == "DFS": for row in range(len(self.data)): for col in range(len(self.data[row])): ans = max(ans, self.dfs(row, col, {})) elif algorithm == 'DFS-I': for row in range(len(self.data)): for col in range(len(self.data[row])): ans = max(ans, self.dfs_iterative(row, col)) elif algorithm == 'BFS': for row in range(len(self.data)): for col in range(len(self.data[row])): ans = max(ans, self.bfs(row, col)) else: raise Exception('unknown algorithm ' + algorithm) return ans def neighbours(self, row, col): P = [[0, -1], [0, 1], [1, 0], [-1, 0]] n = [] for p in P: if self.data[row][col] == self.data_at(row + p[0], col + p[1]): n.append((row + p[0], col + p[1])) return n def data_at(self, row, col): if row >= 0 and row < len(self.data) and \ col >= 0 and col < len(self.data[row]): return self.data[row][col] # returning -1 to indicate invalid coordinates return -1 def dfs_iterative(self, row, col): st = [(row, col)] visited = {} ans = 0 while len(st): cur = st.pop() key = str(cur[0]) + "," + str(cur[1]) if key in visited: continue visited[key] = True ans += 1 for n in self.neighbours(cur[0], cur[1]): st.append((n[0], n[1])) return ans def dfs(self, row, col, visited): key = str(row) + "," + str(col) if key in visited: return 0 visited[key] = True ans = 1 for n in self.neighbours(row, col): ans += self.dfs(n[0], n[1], visited) return ans def bfs(self, row, col): q = [(row, col)] visited = {} ans = 0 while len(q): cur = q.pop(0) # deque the front element key = str(cur[0]) + "," + str(cur[1]) if key in visited: continue visited[key] = True ans += 1 for n in self.neighbours(cur[0], cur[1]): # push it to the end of the queue q.append((n[0], n[1])) return ans if __name__ == '__main__': grid = [[1, 0, 1, 1], [0, 1, 1, 0], [1, 0, 1, 1], [1, 0, 1, 1] ] G = Grid(grid) # all the below should print same answer - 8 print(G.max_connected_grid("DFS")) print(G.max_connected_grid("DFS-I")) print(G.max_connected_grid("BFS")) |
class Grid: def __init__(self, data): self.data = data def max_connected_grid(self, algorithm): ans = 1 if algorithm == "DFS": for row in range(len(self.data)): for col in range(len(self.data[row])): ans = max(ans, self.dfs(row, col, {})) elif algorithm == 'DFS-I': for row in range(len(self.data)): for col in range(len(self.data[row])): ans = max(ans, self.dfs_iterative(row, col)) elif algorithm == 'BFS': for row in range(len(self.data)): for col in range(len(self.data[row])): ans = max(ans, self.bfs(row, col)) else: raise Exception('unknown algorithm ' + algorithm) return ans def neighbours(self, row, col): P = [[0, -1], [0, 1], [1, 0], [-1, 0]] n = [] for p in P: if self.data[row][col] == self.data_at(row + p[0], col + p[1]): n.append((row + p[0], col + p[1])) return n def data_at(self, row, col): if row >= 0 and row < len(self.data) and \ col >= 0 and col < len(self.data[row]): return self.data[row][col] # returning -1 to indicate invalid coordinates return -1 def dfs_iterative(self, row, col): st = [(row, col)] visited = {} ans = 0 while len(st): cur = st.pop() key = str(cur[0]) + "," + str(cur[1]) if key in visited: continue visited[key] = True ans += 1 for n in self.neighbours(cur[0], cur[1]): st.append((n[0], n[1])) return ans def dfs(self, row, col, visited): key = str(row) + "," + str(col) if key in visited: return 0 visited[key] = True ans = 1 for n in self.neighbours(row, col): ans += self.dfs(n[0], n[1], visited) return ans def bfs(self, row, col): q = [(row, col)] visited = {} ans = 0 while len(q): cur = q.pop(0) # deque the front element key = str(cur[0]) + "," + str(cur[1]) if key in visited: continue visited[key] = True ans += 1 for n in self.neighbours(cur[0], cur[1]): # push it to the end of the queue q.append((n[0], n[1])) return ans if __name__ == '__main__': grid = [[1, 0, 1, 1], [0, 1, 1, 0], [1, 0, 1, 1], [1, 0, 1, 1] ] G = Grid(grid) # all the below should print same answer - 8 print(G.max_connected_grid("DFS")) print(G.max_connected_grid("DFS-I")) print(G.max_connected_grid("BFS"))
–EOF (The Ultimate Computing & Technology Blog) —
推荐阅读:How to Determine the Leap Year? How to Determine the Armstrong Number? Facebook Recommends Psychiatrist’s Friends Connect, Completely V 3 Link Building Methods Every Blogger Should Be Using 3 Types of Sticky Content Bloggers Should Be Using The Basics of Building an SEO-Friendly Site with Wix Social Media Image Sizes Cheat Sheet for 2016 The Blogging Evolution: From Hobby To Marketing Tool We Want Plates! Food Blogger Criticizes Quirky Food Serving Tren Company Blog Tips: How To Keep Your Company Blog Out Of A Rut
- 评论列表
-
- 添加评论