Skip to content

200. Number of Islands #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions lc200.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# step1
思考ログ
- 前コードレビューを適当にしていたときに見たことがある, 0と1をそのまま扱っても読者にとって親切ではないから、 1をLAND, 0をWATERと表す.
- 再帰を使って解いていた覚えがあるので考えてみるがよくわからなかった, 回答を見る

```python
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
WATER = "0"
LAND = "1"
visited_island = set()
num_rows = len(grid)
num_columns = len(grid[0])

def check_island(i, j):
return 0 <= i < num_rows and 0 <= j < num_columns

def traverse_island(i, j):
island = deque([(i, j)])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

island は land の集合を表すため、不適切であるように感じます。 lands としたらどうかと思いましたが、 land は不加算名詞のため、 s を付けるのは不適切のようです。
自分はよく frontier という言葉を使います。いかがでしょうか?

while island:
row, column = island.popleft()
for i, j in ((-1, 0), (1, 0), (0, -1), (0, 1)):
if not check_island(row + i, column + j):
continue
elif grid[row + i][column + j] == WATER:
continue
elif (row + i, column + j) in visited_island:
continue

visited_island.add((row + i, column + j))
island.append((row + i, column + j))

num_island = 0
for i in range(num_rows):
for j in range(num_columns):
if grid[i][j] == LAND and (i, j) not in visited_island:
traverse_island(i, j)
num_island += 1

return num_island
```

# step2
参考にした方のPR
- https://github.com/potrue/leetcode/pull/18/files#diff-39510a57d6df66ebf67e9f7357544073fc641f48ca67477c4b834fd5789ff6fc
- https://github.com/tokuhirat/LeetCode/pull/17/files#diff-7833dd6e6b257593112f19837a5e20bc769c050d6bf8cbed8f5fdbabfac22c75

- 基本的には, dfsとbfsのどちらかで解く,step1はbfsだったので以下でdfsで解いて見た
- 再帰になんとなく苦手意識があったがstep1と書いていることはほとんど変わらず,なんならこっちの方が書きやすいと感じた.
```python
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
WATER = "0"
LAND = "1"
visited_island = set()

num_rows = len(grid)
num_columns = len(grid[0])

def traverse_island(i, j):
if (i, j) in visited_island:
return
if i < 0 or num_rows <= i or j < 0 or num_columns <= j:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

step3 で書いている条件の否定の書き方が個人的に好きです。
not (0 <= row < num_rows and 0 <= column < num_columns)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

引用しておきます。
https://docs.python.org/3/reference/expressions.html#comparisons

Comparisons can be chained arbitrarily, e.g., x < y <= z is equivalent to x < y and y <= z, except that y is evaluated only once (but in both cases z is not evaluated at all when x < y is found to be false).

return
if grid[i][j] == WATER:
return

visited_island.add((i, j))

traverse_island(i + 1, j)
traverse_island(i - 1, j)
traverse_island(i, j + 1)
traverse_island(i, j - 1)

num_island = 0
for i in range(num_rows):
for j in range(num_columns):
if grid[i][j] == LAND and (i, j) not in visited_island:
traverse_island(i, j)
num_island += 1

return num_island
```
- ごちゃごちゃになっていたが、dfsとbfsでそれぞれ再帰を使う方法, 使わない方法がある. 使わない場合, 前者はstack, 後者はqueueを使うべきであるということを明示的に記録しておこうと思う



- https://github.com/olsen-blue/Arai60/pull/17/files
この方の思考ログで"これはvisitedでグリッド管理するのではなく、陸地を見つけたら島1つ丸ごと消し去って海にしていくイメージが好き。(逆に、池の数を求める場合は、池を埋め立てて陸にする。)"と書かれていて, 確かに面白い発想だなと思った一方で, 小田さんが["入力を破壊していいかどうかは状況次第"](https://github.com/olsen-blue/Arai60/pull/17/files#r1915967908)とおっしゃっていてそちらにも納得した. 小田さんのコメントを意識した上で, この方のような発想を持てたら良いと感じた.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

プログラムを書くというのは、「それを呼び出す人のために何かをしてあげる」ということなのです。(エンジニアかもしれないし、ウェブの向こうのユーザーかもしれません。)
なので、そこまで遠くまで考えてみましょうということかと思います。


# step 3:
- step1と2でi, jを重複して定義していた部分を修正.
- この問題に限って言えば再帰の方がすっきり見える
```python
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
WATER = "0"
visited_island = set()
num_rows = len(grid)
num_columns = len(grid[0])

def inside_island(row, column):
return 0 <= row < num_rows and 0 <= column < num_columns

def traverse_island(row, column):
if not inside_island(row, column):
return
elif grid[row][column] == WATER:
return
elif (row, column) in visited_island:
return

visited_island.add((row, column))

traverse_island(row + 1, column)
traverse_island(row - 1, column)
traverse_island(row, column + 1)
traverse_island(row, column - 1)

num_islands = 0
for i in range(num_rows):
for j in range(num_columns):
if grid[i][j] == WATER:
continue
elif (i, j) in visited_island:
continue

traverse_island(i, j)
num_islands += 1

return num_islands
```

```python
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
WATER = "0"
visited_island = set()
num_rows = len(grid)
num_columns = len(grid[0])

def inside_island(row, column):
return 0 <= row < num_rows and 0 <= column < num_columns
Copy link

@potrue potrue May 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

こちら、たぶん"island(島)"というと連続したlandからなるいくつかのマス目の集合、というイメージになるので、単にgridの範囲内にあるかどうかだけであればis_inside_gridとかにしてしまった方がよいと思います。

また、この関数にgrid[row][column] != WATERおよび(row, column) not in visited_islandの条件もまとめてしまって、下の二つのfor文で使ってもよいと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確かにこうしてみると、island, landそれと全体的なマップ(?)がごちゃごちゃしていますね。is_inside_grid良い気がしてきました


def traverse_island(row, column):
island = deque([(row, column)])

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

traverse_island 呼び出し時の row と column は visited_island に追加されないまま探索が始まるので、これだけ二回キューに入れられるのかなと思いました。個人的にはここでも visited_island に追加しておきたい気持ちがありました。


while island:
r, c = island.popleft()
for (i, j) in ((1, 0), (-1, 0), (0, 1), (0, -1)):
if not inside_island(r + i, c + j):
continue
elif grid[r + i][c + j] == WATER:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この辺の elif は私は if で書きます。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

書きながら思っていたのですが、まあいいかとelifを使っていました。

elifを使う場合はそれが条件文として対応している場合に限定した方が良いですね

continue
elif (r + i, c + j) in visited_island:
continue

visited_island.add((r + i, c + j))
island.append((r + i, c + j))

num_islands = 0
for i in range(num_rows):
for j in range(num_columns):
if grid[i][j] == WATER:
continue
elif (i, j) in visited_island:
continue

traverse_island(i, j)
num_islands += 1

return num_islands
```

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inside_island について potrue さんと同じ意見です。
細かいですが、island = deque([(row, column)]) は island を構成する陸が格納されているのでわかりますが、visited_island は visited_lands の方が適切な感じを受けました。

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

islandとlandについて、あまり深く考えていませんでしたがその通りですね