Skip to content
Open
Show file tree
Hide file tree
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
52 changes: 52 additions & 0 deletions arai60/number-of-islands/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
## 考察
- 何回か解いたことあり
- つまり、抽象化すると陸をノードとするグラフの連結成分の数を求めればOK
- 方針
- DFS
- BFS
- Union Find
- 昔この問題をUnion Findでやってみたことがあるが、あまり綺麗に書けなかった記憶
- 常識の範囲内に入っているか気になる
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

- まずは一番実装しやすいDFSでやる
- あとは実装

## Step1
- DFSで実装した
- time: O(mn), space: O(mn)
- 気になった点
- 関数内に関数を書く書き方をよく使っているけど、これ大丈夫?
- 実務ではこの書き方使ったことない

## Step2
- 他の人のPRを検索
- dfsという名前は使わないように
- よくない癖が付いてた。。
- Ref. https://discord.com/channels/1084280443945353267/1231966485610758196/1236231890038689844
- 元のgridを破壊的に変更して陸を沈めていく方法もあるのか
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

deepcopy する手もあります。入力を破壊していいかと、出力を破壊されていいかは気にしておきましょう。
https://discord.com/channels/1084280443945353267/1252267683731345438/1252556045524537344

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

訪問済みを管理する方法についてはたくさんの議論があるので、全部選択肢としてぱっと出るようにしておきます。
入力を破壊していいかは面接官に確認した方が良さそうです。

- メモリは抑えられるが、バグりそう
- 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つ渡すようにした
- 方向を行と列で分けて書いていたけど、ペアにしてみた
34 changes: 34 additions & 0 deletions arai60/number-of-islands/step1.py
Original file line number Diff line number Diff line change
@@ -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
40 changes: 40 additions & 0 deletions arai60/number-of-islands/step2.py
Original file line number Diff line number Diff line change
@@ -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()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

これもdfsに近いものを感じます。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

dequeを使う必要があるか気になりました。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

関数名よりはたしかに変数のほうが、構わない(変数名が i でもいい場合がある)んですが、上から読んでいってできるだけパズルを作らないというのが大事です。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

list でいいならそっちのほうがいいですね。パズル度が下がるので。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@TORUS0818 @oda
レビューありがとうございます。listだと先頭からのpopが少し遅いため、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
35 changes: 35 additions & 0 deletions arai60/number-of-islands/step3.py
Original file line number Diff line number Diff line change
@@ -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]:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

最初(10行目)にこの判定を持ってきても良いかなと思いました。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

とりあえず渡しておいて、次の人に判定を任せるでもOKですね。

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:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

長くなるようなら、ここら辺の条件チェックを関数として切り出すと良いかなと思いました。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます。
名前を付ける目的であれば、変数に入れてあげてもいいかもです。

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
26 changes: 26 additions & 0 deletions arai60/number-of-islands/step4.py
Original file line number Diff line number Diff line change
@@ -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