-
Notifications
You must be signed in to change notification settings - Fork 0
733. Flood Fill #76
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?
733. Flood Fill #76
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 |
|---|---|---|
|
|
@@ -2,12 +2,161 @@ | |
|
|
||
| ## アプローチ | ||
|
|
||
| * `sr`, `sc`をスタートとして, 隣接する色が同じセルを`color`に変えていく | ||
| * BFSまたはDFSでできる | ||
| * BFSの方がより現実の動作に近いからBFSで実装する | ||
| * 再帰でもループでもいいが練習のためループで実装する | ||
| * 全部のセルを見る場合が最大の実行時間になる | ||
| * 50 * 50 / 10^6 ~= 10^-3 sec程度 | ||
| * 2:50 | ||
|
|
||
| ## Code1-1 | ||
|
|
||
| * 8:02 | ||
|
|
||
| ```python | ||
| import copy | ||
|
|
||
|
|
||
| class Solution: | ||
| def floodFill(self, image: List[List[int]], sr: int, sc: int, color: int) -> List[List[int]]: | ||
| image = copy.deepcopy(image) | ||
|
|
||
| num_rows = len(image) | ||
| num_cols = len(image[0]) | ||
| if (sr < 0 or num_rows <= sr) or (sc < 0 or num_cols <= sc): | ||
| return image | ||
|
|
||
| original_color = image[sr][sc] | ||
|
|
||
| frontier = [(sr, sc)] | ||
| visited = set() | ||
| while frontier: | ||
| next_frontier = [] | ||
| for r, c in frontier: | ||
| visited.add((r, c)) | ||
| image[r][c] = color | ||
| for dr, dc in [(1, 0), (0, 1), (-1, 0), (0, -1)]: | ||
| new_r = r + dr | ||
| new_c = c + dc | ||
| if (new_r < 0 or num_rows <= new_r) or (new_c < 0 or num_cols <= new_c): | ||
| continue | ||
| if (new_r, new_c) in visited: | ||
| continue | ||
| if image[new_r][new_c] != original_color: | ||
| continue | ||
| next_frontier.append((new_r, new_c)) | ||
| frontier = next_frontier | ||
|
|
||
| return image | ||
|
|
||
| ``` | ||
|
|
||
| # Step2 | ||
|
|
||
| ## Code2-1 | ||
|
|
||
| * 範囲に入っているかどうかの判定を関数に切り出した | ||
|
|
||
| ```python | ||
| import copy | ||
|
|
||
|
|
||
| class Solution: | ||
| def floodFill(self, image: List[List[int]], sr: int, sc: int, color: int) -> List[List[int]]: | ||
| def is_in_image(row: int, col: int): | ||
| if row < 0 or num_rows <= row: | ||
| return False | ||
| if col < 0 or num_cols <= col: | ||
| return False | ||
| return True | ||
|
|
||
| image = copy.deepcopy(image) | ||
|
|
||
| num_rows = len(image) | ||
| num_cols = len(image[0]) | ||
| if not is_in_image(sr, sc): | ||
| return image | ||
|
|
||
| original_color = image[sr][sc] | ||
|
|
||
| frontier = [(sr, sc)] | ||
| visited = set() | ||
| while frontier: | ||
| next_frontier = [] | ||
| for r, c in frontier: | ||
| visited.add((r, c)) | ||
| image[r][c] = color | ||
| for dr, dc in [(1, 0), (0, 1), (-1, 0), (0, -1)]: | ||
| new_r = r + dr | ||
| new_c = c + dc | ||
| if not is_in_image(new_r, new_c): | ||
| continue | ||
| if (new_r, new_c) in visited: | ||
| continue | ||
| if image[new_r][new_c] != original_color: | ||
| continue | ||
| next_frontier.append((new_r, new_c)) | ||
| frontier = next_frontier | ||
|
|
||
| return image | ||
|
|
||
| ``` | ||
|
|
||
| ## 他の解法を見る | ||
|
|
||
| * 再帰(dfs)が多かった | ||
|
|
||
| # Step3 | ||
|
|
||
| ## Code3-1 | ||
|
|
||
| * 6:09 | ||
| * 4:33 | ||
| * 3:07 | ||
|
|
||
| ```python | ||
| import copy | ||
|
|
||
|
|
||
| class Solution: | ||
| def floodFill(self, image: List[List[int]], sr: int, sc: int, color: int) -> List[List[int]]: | ||
| image_copy = copy.deepcopy(image) | ||
|
|
||
| num_rows = len(image_copy) | ||
| num_cols = len(image_copy[0]) | ||
|
|
||
| def is_in_image(row: int, col: int) -> bool: | ||
| if row < 0 or num_rows <= row: | ||
| return False | ||
| if col < 0 or num_cols <= col: | ||
| return False | ||
| return True | ||
|
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. 配列外参照していないかどうかの判定であり、比較的すぐわかりやすい判定の部類だと思うので、early return などせず単に return 0 <= row < num_rows and 0 <= col < num_colとしてしまっても差し障りないとも思います。
Owner
Author
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. たしかに、簡潔で良さそうです |
||
|
|
||
| if not is_in_image(sr, sc): | ||
| return image_copy | ||
|
|
||
| original_color = image_copy[sr][sc] | ||
|
|
||
| coordinates = [(sr, sc)] | ||
|
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. "floodFill操作により色をcolorへと変更するピクセルの" インデックス(座標)なので、"~"の部分の意味合いを変数名に入れても良いと思いました。
Owner
Author
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. coordinates_to_fillなどですかね。 |
||
| visited = set() | ||
| while coordinates: | ||
|
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. 提案というより質問なのですが、こちらのコードのように2つのlist を用いてBFSを実装するのはどのような理由からでしょうか? 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. BFS を deque でというのはこの話ですかね。 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. 想定していた実装はリンク先で扱われている実装とは異なっているように思います. import copy
import collections
class Solution:
def floodFill(self, image: List[List[int]], sr: int, sc: int, color: int) -> List[List[int]]:
# (前略)
original_color = image_copy[sr][sc]
coordinates = collections.deque([(sr, sc)]) # deque へ変更
visited = set()
while coordinates:
r, c = coordinates.popleft() # r, c を deque 先頭から取り出すように変更
visited.add((r, c))
image_copy[r][c] = color
for dr, dc in [(1, 0), (0, 1), (-1, 0), (0, -1)]:
new_r = r + dr
new_c = c + dc
if not is_in_image(new_r, new_c):
continue
if (new_r, new_c) in visited:
continue
if image_copy[new_r][new_c] != original_color:
continue
coordinates.append((new_r, new_c)) # deque 末尾へ直接追加するように変更
return image_copy
Owner
Author
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. これのことですかね? 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. はい,頂いたリンク先のnodchipさんのコメントの以下の部分;
の「deque の中に要素とレベルの tuple を入れるやり方」と僕からお送りした実装はほとんど等価で,deque に要素のみを入れるやり方です. 質問したときに考えていたことを話すと,
ぐらいのことを考えていました.2点目の背景があったので1点目が気になったという構図です.
Owner
Author
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. 2点目に関して、自分もよく見かける実装は一つのdequeを引き回す形ですね。 自分は、二つの配列を使ってレベルごとにbfsをする方法がわかりやすいと思ったのでそちらを採用しています。 そして、1点目に関連するのですが、自分の書くコードに一貫性を持たせるために、今回はレベルごとに処理が変わるわけでなくても、レベルごとに配列を用意する方法にしました 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. 分かりやすさと(今回のコードに限らない)一貫性の2点からレベルごとに配列を用意するようにされたのですね。 ご回答ありがとうございました、自分でもやり取りの中で色々整理できました🙇 (長くなってすみません) |
||
| next_coordinates = [] | ||
| for r, c in coordinates: | ||
| visited.add((r, c)) | ||
| image_copy[r][c] = color | ||
| for dr, dc in [(1, 0), (0, 1), (-1, 0), (0, -1)]: | ||
| new_r = r + dr | ||
| new_c = c + dc | ||
| if not is_in_image(new_r, new_c): | ||
| continue | ||
| if (new_r, new_c) in visited: | ||
| continue | ||
| if image_copy[new_r][new_c] != original_color: | ||
| continue | ||
| next_coordinates.append((new_r, new_c)) | ||
| coordinates = next_coordinates | ||
|
|
||
| return image_copy | ||
|
|
||
| ``` | ||
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.
image が長さ0である場合も考慮して良いと思いました。
引数sr,sc が配列外参照している場合も考慮して点からも、こちらの場合を考慮する方が粒度が揃う(あり得る配列外参照を網羅できる)ので。
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.
確かにその方が良さそうです