diff --git a/arai60/number-of-islands/README.md b/arai60/number-of-islands/README.md new file mode 100644 index 0000000..0ea2215 --- /dev/null +++ b/arai60/number-of-islands/README.md @@ -0,0 +1,52 @@ +## 考察 +- 何回か解いたことあり +- つまり、抽象化すると陸をノードとするグラフの連結成分の数を求めればOK +- 方針 + - DFS + - BFS + - Union Find + - 昔この問題をUnion Findでやってみたことがあるが、あまり綺麗に書けなかった記憶 + - 常識の範囲内に入っているか気になる +- まずは一番実装しやすいDFSでやる +- あとは実装 + +## Step1 +- DFSで実装した +- time: O(mn), space: O(mn) +- 気になった点 + - 関数内に関数を書く書き方をよく使っているけど、これ大丈夫? + - 実務ではこの書き方使ったことない + +## Step2 +- 他の人のPRを検索 +- dfsという名前は使わないように + - よくない癖が付いてた。。 + - Ref. https://discord.com/channels/1084280443945353267/1231966485610758196/1236231890038689844 +- 元のgridを破壊的に変更して陸を沈めていく方法もあるのか + - メモリは抑えられるが、バグりそう + - Ref. https://github.com/hayashi-ay/leetcode/pull/33/files#r1648958531 +- LANDとWATERはenum的に定義しておくと可読性良さそう + - Pythonのenumはパフォーマンスが悪いみたいなのをどっかで見た気がする + - でもまあそれがボトルネックになることは考えられない + - ここでは定数として定義しておくのが良さそう + - Ref. https://github.com/hayashi-ay/leetcode/pull/33/files#diff-225d97de8f43ab6e115258cc7d483e5a343d8556774b2dde47c392b38b49e89bR37 +- 関数内に関数を書くのはとくにコメント付いてないので大丈夫そう +- Union Find実装もある + - Ref. https://github.com/hayashi-ay/leetcode/pull/33/files#diff-225d97de8f43ab6e115258cc7d483e5a343d8556774b2dde47c392b38b49e89bR98 +- あとはBFSでもやってみる + - `step2.py` へ追加 + +## Step3 +- 1回目: 5m37s +- 2回目: 5m12s +- 3回目: 4m51s + +## Step4 +- レビューを元に修正 +- 変数名の見直し + - n_row -> num_rows + - n_col -> num_cols + - drow -> delta_row + - dcol -> delta_col +- explore_islandの引数にtupleを渡すのではなく、intを2つ渡すようにした +- 方向を行と列で分けて書いていたけど、ペアにしてみた diff --git a/arai60/number-of-islands/step1.py b/arai60/number-of-islands/step1.py new file mode 100644 index 0000000..7e3711f --- /dev/null +++ b/arai60/number-of-islands/step1.py @@ -0,0 +1,34 @@ +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + m = len(grid) + n = len(grid[0]) + visited = [[False] * n for _ in range(m)] + + def dfs(node: tuple): + current_row, current_col = node + visited[current_row][current_col] = True + + for next_node in get_neighbors(node): + next_row, next_col = next_node + if not visited[next_row][next_col]: + dfs(next_node) + + def get_neighbors(node: tuple): + drow = (0, 1, 0, -1) + dcol = (1, 0, -1, 0) + neighbors = [] + for direction in range(4): + next_row = node[0] + drow[direction] + next_col = node[1] + dcol[direction] + if 0 <= next_row < m and 0 <= next_col < n and grid[next_row][next_col] == '1': + neighbors.append((next_row, next_col)) + return neighbors + + total = 0 + for i in range(m): + for j in range(n): + if grid[i][j] == '1' and not visited[i][j]: + dfs((i, j)) + total += 1 + + return total diff --git a/arai60/number-of-islands/step2.py b/arai60/number-of-islands/step2.py new file mode 100644 index 0000000..b1eb4dd --- /dev/null +++ b/arai60/number-of-islands/step2.py @@ -0,0 +1,40 @@ +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + LAND = '1' + WATER = '0' + n_row = len(grid) + n_col = len(grid[0]) + visited = [[False] * n_col for _ in range(n_row)] + + def explore_island(node: tuple): + que = deque() + que.append(node) + while que: + current_node = que.popleft() + current_row, current_col = current_node + visited[current_row][current_col] = True + for next_node in get_neighbors(current_node): + next_row, next_col = next_node + if not visited[next_row][next_col]: + que.append(next_node) + visited[next_row][next_col] = True + + def get_neighbors(node: tuple): + drow = (0, 1, 0, -1) + dcol = (1, 0, -1, 0) + neighbors = [] + for direction in range(4): + next_row = node[0] + drow[direction] + next_col = node[1] + dcol[direction] + if 0 <= next_row < n_row and 0 <= next_col < n_col and grid[next_row][next_col] == LAND: + neighbors.append((next_row, next_col)) + return neighbors + + total = 0 + for i in range(n_row): + for j in range(n_col): + if grid[i][j] == LAND and not visited[i][j]: + explore_island((i, j)) + total += 1 + + return total diff --git a/arai60/number-of-islands/step3.py b/arai60/number-of-islands/step3.py new file mode 100644 index 0000000..da19741 --- /dev/null +++ b/arai60/number-of-islands/step3.py @@ -0,0 +1,35 @@ +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + LAND = '1' + WATER = '0' + n_row = len(grid) + n_col = len(grid[0]) + visited = [[False] * n_col for _ in range(n_row)] + + def explore_island(node: tuple) -> None: + current_row, current_col = node + visited[current_row][current_col] = True + for next_node in get_neighbors(node): + next_row, next_col = next_node + if not visited[next_row][next_col]: + explore_island(next_node) + + def get_neighbors(node: tuple) -> list: + drow = (0, 1, 0, -1) + dcol = (1, 0, -1, 0) + neighbors = [] + for direction in range(4): + next_row = node[0] + drow[direction] + next_col = node[1] + dcol[direction] + if 0 <= next_row < n_row and 0 <= next_col < n_col and grid[next_row][next_col] == LAND: + neighbors.append((next_row, next_col)) + + return neighbors + + total = 0 + for i in range(n_row): + for j in range(n_col): + if grid[i][j] == LAND and not visited[i][j]: + explore_island((i, j)) + total += 1 + return total diff --git a/arai60/number-of-islands/step4.py b/arai60/number-of-islands/step4.py new file mode 100644 index 0000000..cfcb48f --- /dev/null +++ b/arai60/number-of-islands/step4.py @@ -0,0 +1,26 @@ +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + LAND = '1' + WATER = '0' + num_rows = len(grid) + num_cols = len(grid[0]) + visited = [[False] * num_cols for _ in range(num_rows)] + + def explore_island(row, col): + visited[row][col] = True + directions = [(0, 1), (1, 0), (-1, 0), (0, -1)] + for delta_row, delta_col in directions: + next_row = row + delta_row + next_col = col + delta_col + if not (0 <= next_row < num_rows and 0 <= next_col < num_cols): continue + if grid[next_row][next_col] == WATER: continue + if visited[next_row][next_col]: continue + explore_island(next_row, next_col) + + total = 0 + for i in range(num_rows): + for j in range(num_cols): + if grid[i][j] == LAND and not visited[i][j]: + explore_island(i, j) + total += 1 + return total