From 40bb7c28befaca124f5db46858f700ea5aab6e12 Mon Sep 17 00:00:00 2001 From: aks Date: Sat, 26 Apr 2025 14:39:06 +0200 Subject: [PATCH 1/3] docs: Add docstrings and doctests for Matrix methods - Added descriptive docstrings for Matrix, is_safe, count_islands, and diffs methods - Added doctests covering both normal and edge cases for all methods - Improves didactic quality and test coverage --- matrix/count_islands_in_matrix.py | 144 +++++++++++++++++++++++++++--- 1 file changed, 130 insertions(+), 14 deletions(-) diff --git a/matrix/count_islands_in_matrix.py b/matrix/count_islands_in_matrix.py index 64c595e8499d..b8735dd48f9c 100644 --- a/matrix/count_islands_in_matrix.py +++ b/matrix/count_islands_in_matrix.py @@ -1,15 +1,88 @@ -# An island in matrix is a group of linked areas, all having the same value. -# This code counts number of islands in a given matrix, with including diagonal -# connections. +class Matrix: + """ + Class to represent a 2D grid as a matrix and count the number of islands. + An island is a group of connected 1s (connections can be vertical, horizontal, or diagonal). + """ - -class Matrix: # Public class to implement a graph def __init__(self, row: int, col: int, graph: list[list[bool]]) -> None: + """ + Initialize the matrix. + + :param row: Number of rows + :param col: Number of columns + :param graph: 2D list representing the matrix (1 = land, 0 = water) + + >>> graph = [[1, 1, 0], [0, 1, 0], [1, 0, 0]] + >>> m = Matrix(3, 3, graph) + >>> m.ROW + 3 + >>> m.COL + 3 + >>> m.graph == graph + True + + # Edge case: Empty matrix + >>> empty = Matrix(0, 0, []) + >>> empty.ROW + 0 + >>> empty.COL + 0 + >>> empty.graph + [] + + # Edge case: 1x1 matrix + >>> single = Matrix(1, 1, [[1]]) + >>> single.ROW + 1 + >>> single.COL + 1 + >>> single.graph + [[1]] + """ + self.ROW = row self.COL = col self.graph = graph + + def is_safe(self, i: int, j: int, visited: list[list[bool]]) -> bool: + """ + Check if a cell (i, j) is "safe". + + We consider a cell "safe" if: + - It is within the bounds of the matrix + - It has not been visited yet + - It contains land (i.e., value is nonzero) + + :param i: row index + :param j: column index + :param visitied: 2D list indicating which cells we have visited + :return: True if cell is safe, else False + + >>> m = Matrix(3, 3, [[1, 0, 0], [0, 0, 1]]) + >>> visited = [[False]*3 for _ in range(3)] + + # Within bounds, unvisited, and value is 1 (land) + >>> m.is_safe(0, 0, visited) + True + + # Within bounds, unvisited, but value is 0 (water) + >>> m.is_safe(0, 1, visited) + False + + # Within bounds, already visited + >>> visited[0][0] = True + >>> m.is_safe(0, 0, visited) + False + + # Out of bounds + >>> m.is_safe(3, 0, visited) + False + >>> m.is_safe(-1, 0, visited) + False + """ + return ( 0 <= i < self.ROW and 0 <= j < self.COL @@ -18,20 +91,63 @@ def is_safe(self, i: int, j: int, visited: list[list[bool]]) -> bool: ) def diffs(self, i: int, j: int, visited: list[list[bool]]) -> None: - # Checking all 8 elements surrounding nth element - row_nbr = [-1, -1, -1, 0, 0, 1, 1, 1] # Coordinate order + """ + Depth-first search (DFS) to mark all land cells (1s) connected to (i, j) as visited. + + :param i: Row index of current cell + :param j: Column index of current cell + :param visited: 2D list tracking visited cells + + diffs() checks the 8 cells surrounding the current cell (i, j) and marks the land cells as visited. + + >>> graph = [[1, 1, 0], [0, 1, 0], [0, 0, 1]] + >>> m = Matrix(3, 3, graph) + >>> visited = [[False] * 3 for _ in range(3)] + >>> m.diffs(0, 0, visited) + >>> visited[0][0] and visited[0][1] and visited[1][1] + True + >>> visited[2][2] + False + """ + + # 8 possible movements (N, NE, E, SE, S, SW, W, NW) + row_nbr = [-1, -1, -1, 0, 0, 1, 1, 1] col_nbr = [-1, 0, 1, -1, 1, -1, 0, 1] - visited[i][j] = True # Make those cells visited + + # Make cells visited + visited[i][j] = True for k in range(8): - if self.is_safe(i + row_nbr[k], j + col_nbr[k], visited): - self.diffs(i + row_nbr[k], j + col_nbr[k], visited) + next_i, next_j = i + row_nbr[k], j + col_nbr[k] + if self.is_safe(next_i, next_j, visited): + self.diffs(next_i, next_j, visited) + + def count_islands(self) -> int: + """ + Count the number of islands in the matrix. - def count_islands(self) -> int: # And finally, count all islands. - visited = [[False for j in range(self.COL)] for i in range(self.ROW)] + :return: Number of islands found + + >>> graph[[1, 1, 0], [0, 1, 0], [1, 0, 0]] + >>> m = Matrix(3, 3, graph) + >>> m.count_islands() + 2 + + >>> graph2 = [[0, 0, 0], [0, 0, 0]] + >>> m2 = Matrix(2, 3, graph2) + >>> m2.count_islands() + 0 + + >>> graph3 = [[1]] + >>> m3 = Matrix(1, 1, graph3) + >>> m3.count_islands() + 1 + """ + visited = [[False for _ in range(self.COL)] for _ in range(self.ROW)] count = 0 for i in range(self.ROW): for j in range(self.COL): - if visited[i][j] is False and self.graph[i][j] == 1: + if not visited[i][j] and self.graph[i][j] == 1: self.diffs(i, j, visited) - count += 1 + count += 1 return count + From 3244ef99e654cdb5896cdb7e7ad9b7e875a3d022 Mon Sep 17 00:00:00 2001 From: aks Date: Mon, 28 Apr 2025 17:56:56 +0200 Subject: [PATCH 2/3] Clean up formatting and typos - Formatting code with pre-commit hooks (black, ruff) - Fixing typos - No functional changes --- matrix/count_islands_in_matrix.py | 34 +++++++++++++++++-------------- matrix/matrix_class.py | 8 +++++--- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/matrix/count_islands_in_matrix.py b/matrix/count_islands_in_matrix.py index b8735dd48f9c..a7beaad6e64d 100644 --- a/matrix/count_islands_in_matrix.py +++ b/matrix/count_islands_in_matrix.py @@ -1,7 +1,9 @@ class Matrix: """ - Class to represent a 2D grid as a matrix and count the number of islands. - An island is a group of connected 1s (connections can be vertical, horizontal, or diagonal). + Class to represent a 2D binary grid as a matrix and count the number of islands. + An island is a group of connected 1s. + We call cells with 1 "land" and cells with 0 "water". + Connections can be vertical, horizontal, or diagonal. """ def __init__(self, row: int, col: int, graph: list[list[bool]]) -> None: @@ -44,8 +46,6 @@ def __init__(self, row: int, col: int, graph: list[list[bool]]) -> None: self.COL = col self.graph = graph - - def is_safe(self, i: int, j: int, visited: list[list[bool]]) -> bool: """ Check if a cell (i, j) is "safe". @@ -53,14 +53,14 @@ def is_safe(self, i: int, j: int, visited: list[list[bool]]) -> bool: We consider a cell "safe" if: - It is within the bounds of the matrix - It has not been visited yet - - It contains land (i.e., value is nonzero) + - It contains land (i.e., value is 1) :param i: row index :param j: column index :param visitied: 2D list indicating which cells we have visited :return: True if cell is safe, else False - >>> m = Matrix(3, 3, [[1, 0, 0], [0, 0, 1]]) + >>> m = Matrix(3, 3, [[1, 0, 0], [0, 0, 1], [0, 0, 0]]) >>> visited = [[False]*3 for _ in range(3)] # Within bounds, unvisited, and value is 1 (land) @@ -71,7 +71,7 @@ def is_safe(self, i: int, j: int, visited: list[list[bool]]) -> bool: >>> m.is_safe(0, 1, visited) False - # Within bounds, already visited + # Within bounds, land, but already visited >>> visited[0][0] = True >>> m.is_safe(0, 0, visited) False @@ -92,15 +92,15 @@ def is_safe(self, i: int, j: int, visited: list[list[bool]]) -> bool: def diffs(self, i: int, j: int, visited: list[list[bool]]) -> None: """ - Depth-first search (DFS) to mark all land cells (1s) connected to (i, j) as visited. + Check the 8 cells connected to (i, j) and marks the land cells as visited. + + Depth-first search (DFS) to mark land cells connected to (i, j) as visited. :param i: Row index of current cell :param j: Column index of current cell :param visited: 2D list tracking visited cells - diffs() checks the 8 cells surrounding the current cell (i, j) and marks the land cells as visited. - - >>> graph = [[1, 1, 0], [0, 1, 0], [0, 0, 1]] + >>> graph = [[1, 1, 0], [0, 1, 0], [0, 0, 0]] >>> m = Matrix(3, 3, graph) >>> visited = [[False] * 3 for _ in range(3)] >>> m.diffs(0, 0, visited) @@ -127,16 +127,21 @@ def count_islands(self) -> int: :return: Number of islands found - >>> graph[[1, 1, 0], [0, 1, 0], [1, 0, 0]] + >>> graph = [[1, 1, 0], [0, 1, 0], [1, 0, 0]] >>> m = Matrix(3, 3, graph) >>> m.count_islands() + 1 + + >>> graph1 = [[1, 1, 0], [0, 0, 0], [1, 0, 0]] + >>> m1 = Matrix(3, 3, graph1) + >>> m1.count_islands() 2 >>> graph2 = [[0, 0, 0], [0, 0, 0]] >>> m2 = Matrix(2, 3, graph2) >>> m2.count_islands() 0 - + >>> graph3 = [[1]] >>> m3 = Matrix(1, 1, graph3) >>> m3.count_islands() @@ -148,6 +153,5 @@ def count_islands(self) -> int: for j in range(self.COL): if not visited[i][j] and self.graph[i][j] == 1: self.diffs(i, j, visited) - count += 1 + count += 1 return count - diff --git a/matrix/matrix_class.py b/matrix/matrix_class.py index a5940a38e836..230eb95006fa 100644 --- a/matrix/matrix_class.py +++ b/matrix/matrix_class.py @@ -204,9 +204,11 @@ def cofactors(self) -> Matrix: return Matrix( [ [ - self.minors().rows[row][column] - if (row + column) % 2 == 0 - else self.minors().rows[row][column] * -1 + ( + self.minors().rows[row][column] + if (row + column) % 2 == 0 + else self.minors().rows[row][column] * -1 + ) for column in range(self.minors().num_columns) ] for row in range(self.minors().num_rows) From d052b88ebe33684e8ff19e6e4d952d32da3e6425 Mon Sep 17 00:00:00 2001 From: aks Date: Mon, 28 Apr 2025 18:05:20 +0200 Subject: [PATCH 3/3] fix: Ensure is_safe() returns boolean instead of int Previously, is_safe() could return the value of a cell (int 1 or 0), which caused test failures. This fix ensures that is_safe() returns True if the cell is safe to visit and False otherwise --- matrix/count_islands_in_matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matrix/count_islands_in_matrix.py b/matrix/count_islands_in_matrix.py index a7beaad6e64d..401d662b482a 100644 --- a/matrix/count_islands_in_matrix.py +++ b/matrix/count_islands_in_matrix.py @@ -87,7 +87,7 @@ def is_safe(self, i: int, j: int, visited: list[list[bool]]) -> bool: 0 <= i < self.ROW and 0 <= j < self.COL and not visited[i][j] - and self.graph[i][j] + and self.graph[i][j] == 1 ) def diffs(self, i: int, j: int, visited: list[list[bool]]) -> None: