Skip to content

200. Number of Islands.md #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 1 commit 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
159 changes: 159 additions & 0 deletions 200. Number of Islands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
URL: https://leetcode.com/problems/number-of-islands/description/

# Step 1

- 実装時間: 10分
- 高さn、幅mのマップとして、
- 時間計算量: O(nm)
- 空間計算量: O(nm)

```python
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
num_island = 0
Copy link

Choose a reason for hiding this comment

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

Go言語のgoogleスタイルガイドには変数名に型名を入れないようにとあり、num_islandよりisland_countのような変数名が好まれます。Pythonのスタイルガイドに似た記述がないか見てみましたが見つからなかったです。動的型付け言語では許容されるのかもしれません

参考: https://google.github.io/styleguide/go/decisions#variable-names

Omit types and type-like words from most variable names.

Copy link
Owner Author

Choose a reason for hiding this comment

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

ご指摘ありがとうございます!

ご指摘の変数名に型名を入れないほうがいいという感覚は、Pythonでも共通のようです。
sakupan102/arai60-practice#13 (comment)

僕も同じ感覚でして、今回のnum_はpythonでいうところのint型などの整数型のつもりではなく、numberという英単語のつもりでした。ですが、ご指摘の通り、個数を表すならcountの方がより表現できているなと気づきました。(また、numなら伝わるかもとは思いましたが、そもそも英単語の省略もイマイチでしたね。。。)

Choose a reason for hiding this comment

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

変数名として正しくは、number_of_islandsでただこれだと長くてnum_islandsに略してもまあ伝わるかなという感覚です。まあ命名はチームのルールに合わせるのが良いと思うので、チームのみんながnum_が分かりにくいということであれば、countもありだと思います

visited = set()
for row in range(len(grid)):
for column in range(len(grid[0])):
if (row, column) in visited or grid[row][column] == '0':
continue
num_island += 1
islands = [(row, column)]
Copy link

Choose a reason for hiding this comment

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

island(島)は'1'(陸)の集合なので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が関係なくてびっくりした。
https://english-battle.com/word/island

while islands:
island_row, island_column = islands.pop()
if (island_row, island_column) in visited or grid[island_row][island_column] == '0':
Copy link

Choose a reason for hiding this comment

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

このチェックを append() の直前に入れると、 append() と pop() の回数が減り、少し処理が軽くなると思います。ただ、好みの問題かもしれません。

continue

visited.add((island_row, island_column))
for dy, dx in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
Copy link

Choose a reason for hiding this comment

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

ここの順序が気になりました。1,2,3,4の全ての順序を書き出すときに、1234から始めて1243, 1324, 1342, ... のように接頭辞12を固定して、次は接頭辞を13に固定して、、、という風にすることによって抜け漏れがないように工夫すると思います。
なのでここも (0, 1), (0, -1), (1, 0), (-1, 0) の方が自然な順序なのではないかという気がしました。気にしすぎかもしれないです

if 0 <= island_row + dy < len(grid) and 0 <= island_column + dx < len(grid[0]):
islands.append((island_row + dy, island_column + dx))

return num_island
```

# Step 2

- 参考にしたURL
- https://github.com/Hurukawa2121/leetcode/pull/17
- https://github.com/colorbox/leetcode/pull/31
- https://github.com/haniwachann/leetcode/pull/3
- https://github.com/tarinaihitori/leetcode/pull/17
- https://github.com/hroc135/leetcode/pull/17
- https://github.com/seal-azarashi/leetcode/pull/17
- https://github.com/thonda28/leetcode/pull/15
- https://github.com/goto-untrapped/Arai60/pull/38
- https://github.com/Ryotaro25/leetcode_first60/pull/18
- https://github.com/Yoshiki-Iwasa/Arai60/pull/16
- https://github.com/Mike0121/LeetCode/pull/34
- https://github.com/tshimosake/arai60/pull/8
- https://github.com/kazukiii/leetcode/pull/18
- https://github.com/TORUS0818/leetcode/pull/19

- 考えられるアプローチ
- スタックデータ構造
- https://github.com/Hurukawa2121/leetcode/pull/17/files#r1898905955
- BFS
- Union Find
- gridの値に-1(visited)や0を書き込んでいく
- 入力である grid に対する破壊的な変更は避けたほうがよい
- https://github.com/hroc135/leetcode/pull/17/files#r1750534861

- '0'がマジックナンバーとなっているので、定数化かコメントの付与があると読みやすくなりそうです。
- https://github.com/Hurukawa2121/leetcode/pull/17/files#r1898871301

- `visited`という変数名の代わりに`seen`を使ってる。
- https://github.com/Hurukawa2121/leetcode/pull/17/files

- > row, col を使う場合、その最大値として height, width を使うのは違和感があります。
- この感覚は持てていなかったので学び。
- https://github.com/colorbox/leetcode/pull/31/files#r1881784335

- 範囲チェックを関数化するか悩んだけど、1度しか使わないから不要と思ってやらなかった。
- より複雑な場合は、「名前をつける」という意味で関数にしてもいいパターンがありそう。
- https://github.com/colorbox/leetcode/pull/31/files#r1885062583

- 命名に悩んでいた`dx, dy`
- > x, y の代わりに delta_row, delta_col としたほうが分かりやすいと感じました。
- https://github.com/tarinaihitori/leetcode/pull/17#discussion_r1840350347

- `len(grid)`も変数に置いておく
- `number_of_rows = len(grid)`
- https://github.com/tarinaihitori/leetcode/pull/17/files#r1840491336

- traverseの辞書的な意味を知らなかったので調べた。
- to cover or extend over an area or time period.(地域や期間をカバーする、または拡張する。)
- https://www.ei-navi.jp/dictionary/content/traverse/?utm_source=chatgpt.com

- ひとつのセルに対する処理を、関数に分割している人が多かった。
- 他の方のコードを見た後に、自分のコードを見てみるとネストが深い感じがした。
- 書いている時には気にならなかったので、他者の目線がたりない。

```python
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
WATER = '0'
LAND = '1'
Copy link

Choose a reason for hiding this comment

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

この定数が未使用なので消してしまって良さそうですね。

num_island = 0
seen = set()

def traverse(start_row: int, start_column: int) -> None:
islands = [(start_row, start_column)]
while islands:
row, column = islands.pop()
if (row, column) in seen or grid[row][column] == WATER:
continue
seen.add((row, column))
for delta_row, delta_column in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
next_row = row + delta_row
next_column = column + delta_column
if 0 <= next_row < len(grid) and 0 <= next_column < len(grid[0]):
islands.append((next_row, next_column))

for row in range(len(grid)):
for column in range(len(grid[0])):
if (row, column) in seen or grid[row][column] == WATER:
continue
num_island += 1
traverse(row, column)
return num_island
```

# Step 3

- 実装時間: 4分
- 高さn、幅mのマップとして、
- 時間計算量: O(nm)
- 空間計算量: O(nm)

```python
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
num_island = 0
seen = set()
WATER = '0'

def traverse(start_row: int, start_col: int) -> None:
islands = [(start_row, start_col)]
while islands:
row, col = islands.pop()
if (row, col) in seen or grid[row][col] == WATER:
continue
seen.add((row, col))
for delta_row, delta_col in [(0,1), (1,0), (0,-1), (-1,0)]:
Copy link

Choose a reason for hiding this comment

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

個人的には、この四方向を一般化するよりは書き下したほうが読みやすいと思います。
代わりにここの中を関数にするか、簡略化するかして、

append(row, col - 1)
append(row, col + 1)
append(row - 1, col)
append(row + 1, col)

くらいの温度感で書けるといいと思います。

Copy link

Choose a reason for hiding this comment

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

個人的には四方向を一般化する書き方のほうが好きです。現場においては、チームの平均的なメンバーが最短で正確に認知負荷低く読める書き方を選ぶのが良いと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます。常識と好みの範囲が見えた気がします!

Choose a reason for hiding this comment

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

自分もこれくらいの処理なら書き下し派ですかね、8方向見るとか、それぞれに他の共通処理があるなどすれば一般化したくなるかなという感じです

next_row = row + delta_row
next_col = col + delta_col
if 0 <= next_row < len(grid) and 0 <= next_col < len(grid[0]):
Copy link

Choose a reason for hiding this comment

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

このチェックは、pop の後に回すこともできますね。

islands.append((next_row, next_col))

for row in range(len(grid)):
for col in range(len(grid[0])):
if (row, col) in seen or grid[row][col] == WATER:
continue
num_island += 1
traverse(row, col)
Copy link

Choose a reason for hiding this comment

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

traverse もう少し説明的な名前でもいいかもしれません。
関数は中を読まなくても、コードがまともそうであることが分かる名前が好まれます。
日本語でいうと「row, col を含む島を海に変える」くらいじゃないですか。

return num_island
```

- 書いている時に気づいたこと。
- `column`は`col`とすることで、`row`と文字数が揃う。
- 省略は基本避けるがcolは共通認識があって許容範囲だと思った。