-
Notifications
You must be signed in to change notification settings - Fork 0
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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)]) | ||
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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. step3 で書いている条件の否定の書き方が個人的に好きです。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 引用しておきます。
|
||
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)とおっしゃっていてそちらにも納得した. 小田さんのコメントを意識した上で, この方のような発想を持てたら良いと感じた. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. こちら、たぶん"island(島)"というと連続したlandからなるいくつかのマス目の集合、というイメージになるので、単にgridの範囲内にあるかどうかだけであればis_inside_gridとかにしてしまった方がよいと思います。 また、この関数に There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. この辺の elif は私は if で書きます。 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
``` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. inside_island について potrue さんと同じ意見です。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. islandとlandについて、あまり深く考えていませんでしたがその通りですね |
There was a problem hiding this comment.
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 という言葉を使います。いかがでしょうか?