-
Notifications
You must be signed in to change notification settings - Fork 0
695. Max Area of Island #23
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,294 @@ | ||
| ## Step1 | ||
| Number of Islandsと同じようにDFSで解く. 前問とは異なり, 同じノードを2回数えてしまうと答えがずれるのでvisitedやstackへの格納を正確に行うことが重要. | ||
| 時間計算量: O(M ✕ N) | ||
| ```py | ||
| class Solution: | ||
| def maxAreaOfIsland(self, grid: List[List[int]]) -> int: | ||
| if not grid: | ||
| return 0 | ||
|
|
||
| m = len(grid) | ||
| n = len(grid[0]) | ||
| LAND = 1 | ||
| WATER = 0 | ||
| visited = set() | ||
|
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. girdと同じサイズの2D配列でも良いですね。 |
||
| directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] | ||
|
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. 個人的には[(1, 0), (-1, 0), (0, 1), (0, -1)]などの順番の方が意図が明確に感じます。好みの範囲かもしれません。 |
||
| def calculate_area_of_island(x, y): | ||
|
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. グリッドを辿って面積を数えることをcalculateと表現するのは若干違和感があります。explore_island, traverse_islandとかはどうでしょうか? 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. 面積なので個人的には area があって欲しいですね。measure あたりですかねえ。calculate, compute あたりも私はありだと思います。 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.
という縦3,横4のマップに対して と対応させることが多く, |
||
| stack = [(x, y)] | ||
| visited.add((x, y)) | ||
|
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. 好みの問題かとは思いますが, |
||
| area = 1 | ||
| while stack: | ||
| x, y = stack.pop() | ||
|
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. 好みの範囲ですが、x, yよりrow, colの方が自分は好みです。 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. x, y を2次元配列のどの順序で並べるかという面倒な議論があります。 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. パタヘネというコンピュータアーキテクチャの本を読んでおり、DGEMMのアセンブリ実装のあたりでちょうどそのトピックを見かけました!
|
||
| for dx, dy in directions: | ||
| next_x = x + dx | ||
| next_y = y + dy | ||
| if (next_x, next_y) in visited: | ||
| continue | ||
|
|
||
| if not (0 <= next_x < m and 0 <= next_y < n): | ||
| continue | ||
|
|
||
| if grid[next_x][next_y] == WATER: | ||
| continue | ||
|
|
||
| area += 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. pop後にインクリメントする方が自然な気がしますが、これも間違いではないですね。 |
||
| stack.append((next_x, next_y)) | ||
| visited.add((next_x, next_y)) | ||
|
|
||
| return area | ||
|
|
||
| max_area = 0 | ||
| for i in range(m): | ||
| for j in range(n): | ||
| if grid[i][j] == LAND: | ||
| if (i, j) in visited: | ||
|
Comment on lines
+43
to
+44
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 grid[i][j] == LAND and (i, j) not in visited:にしてネストを下げたいですね。 |
||
| continue | ||
|
|
||
| area = calculate_area_of_island(i, j) | ||
| max_area = max(max_area, area) | ||
|
|
||
| return max_area | ||
| ``` | ||
| 前問と同様に再帰DFS, BFS, Union-Findで書き直してみる(復習) | ||
|
|
||
| 1. 再帰DFS | ||
| ポイント | ||
| - visitedに追加するタイミング => visitedとスタックは基本的に同じタイミングで更新する => 非同期キューを考えると二度送信してはいけないとすると、キューへの追加とDBのステータス更新を同時にしないといけない | ||
| - areaをreturnすることで変数の破壊的変更を防ぐ | ||
| - area = calculate_area_of_island(i, j, 1)で面積の初期値は1にする | ||
| 再帰DFSはスタックオーバーフローのリスクがある. 最悪深さO(M ✕ N) | ||
| ```py | ||
| class Solution: | ||
| def maxAreaOfIsland(self, grid: List[List[int]]) -> int: | ||
| if not grid: | ||
| return 0 | ||
|
|
||
| m = len(grid) | ||
| n = len(grid[0]) | ||
| LAND = 1 | ||
| WATER = 0 | ||
| visited = set() | ||
| directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] | ||
| def calculate_area_of_island(x, y, area): | ||
| visited.add((x, y)) | ||
| for dx, dy in directions: | ||
| next_x = x + dx | ||
| next_y = y + dy | ||
| if (next_x, next_y) in visited: | ||
| continue | ||
|
|
||
| if not (0 <= next_x < m and 0 <= next_y < n): | ||
| continue | ||
|
|
||
| if grid[next_x][next_y] == WATER: | ||
| continue | ||
|
|
||
| area += 1 | ||
| # visited.add((next_x, next_y)) ここでaddしても良い | ||
| area = calculate_area_of_island(next_x, next_y, area) | ||
|
|
||
| return area | ||
|
|
||
| max_area = 0 | ||
| for i in range(m): | ||
| for j in range(n): | ||
| if grid[i][j] == LAND: | ||
| if (i, j) in visited: | ||
| continue | ||
|
|
||
| # visited.add((i, j)) | ||
| area = calculate_area_of_island(i, j, 1) | ||
| max_area = max(max_area, area) | ||
|
|
||
| return max_area | ||
| ``` | ||
|
|
||
| 2. BFS | ||
|
|
||
| ```py | ||
| class Solution: | ||
| def maxAreaOfIsland(self, grid: List[List[int]]) -> int: | ||
| if not grid: | ||
| return 0 | ||
|
|
||
| m = len(grid) | ||
| n = len(grid[0]) | ||
| LAND = 1 | ||
| WATER = 0 | ||
| visited = set() | ||
| directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] | ||
| def calculate_area_of_island(x, y, area): | ||
| queue = 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. queueはPythonの標準モジュール名と被るので自分は避けるようにしています。代わりにfrontiersとかはどうでしょうか? 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. 私は結構 queue, stack を使ってしまうのですが、あまり行儀がよくないのはそうですね。 grid_to_visit, cells_to_explore などですかね。 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. nitですが, 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. LeetCodeの実行環境では標準モジュール群がワイルドカードでimport済みなので問題にならないですが、本来はfrom collections import dequeが必要です。 |
||
| queue.append((x, y)) | ||
| visited.add((x, y)) | ||
| while queue: | ||
| x, y = queue.popleft() | ||
| for dx, dy in directions: | ||
| next_x = x + dx | ||
| next_y = y + dy | ||
| if (next_x, next_y) in visited: | ||
| continue | ||
|
|
||
| if not (0 <= next_x < m and 0 <= next_y < n): | ||
| continue | ||
|
|
||
| if grid[next_x][next_y] == WATER: | ||
| continue | ||
|
|
||
| area += 1 | ||
| visited.add((next_x, next_y)) | ||
| queue.append((next_x, next_y)) | ||
|
|
||
| return area | ||
|
|
||
| max_area = 0 | ||
| for i in range(m): | ||
| for j in range(n): | ||
| if grid[i][j] == LAND: | ||
| if (i, j) in visited: | ||
| continue | ||
|
|
||
| area = calculate_area_of_island(i, j, 1) | ||
| max_area = max(max_area, area) | ||
|
|
||
| return max_area | ||
| ``` | ||
| 3. Union-Find | ||
| 時間, 空間計算量: O(M ✕ N). UnionとFindの計算量はアッカーマン関数の逆関数 | ||
| ポイント | ||
| - findで経路圧縮を実装する | ||
|
|
||
| ```py | ||
| class UnionFind: | ||
| def __init__(self, n): | ||
| self.parents = list(range(n)) | ||
| self.area = [0] * n | ||
|
|
||
| # 経路圧縮なし: 計算量はO(logn) | ||
| # def find(self, x): | ||
| # if self.parents[x] == x: | ||
| # return x | ||
|
|
||
| # return self.find(self.parents[x]) | ||
|
|
||
| # 経路圧縮あり: 計算量O(α(n)) | ||
| def find(self, x): | ||
| if self.parents[x] != x: | ||
| self.parents[x] = self.find(self.parents[x]) | ||
|
|
||
| return self.parents[x] | ||
|
|
||
| def union(self, x, y): | ||
| parent_x = self.find(x) | ||
| parent_y = self.find(y) | ||
| if parent_x == parent_y: | ||
| return | ||
|
|
||
| if self.area[parent_x] < self.area[parent_y]: | ||
| parent_x, parent_y = parent_y, parent_x | ||
|
|
||
| self.parents[parent_y] = parent_x | ||
| self.area[parent_x] += self.area[parent_y] | ||
|
|
||
| class Solution: | ||
| def maxAreaOfIsland(self, grid: List[List[int]]) -> int: | ||
| if not grid: | ||
| return 0 | ||
|
|
||
| m = len(grid) | ||
| n = len(grid[0]) | ||
| LAND = 1 | ||
| WATER = 0 | ||
| uf = UnionFind(m * n) | ||
| for i in range(m): | ||
| for j in range(n): | ||
| if grid[i][j] == LAND: | ||
| uf.area[i * n + j] = 1 | ||
|
|
||
| for i in range(m): | ||
| for j in range(n): | ||
| if grid[i][j] == LAND: | ||
| idx = i * n + j | ||
| if j + 1 < n and grid[i][j + 1] == LAND: | ||
| uf.union(idx, idx + 1) | ||
|
|
||
| if i + 1 < m and grid[i + 1][j] == LAND: | ||
| uf.union(idx, idx + n) | ||
|
|
||
| return max(uf.area) | ||
| ``` | ||
| ## Step2 他の人のコードやコメントなどを見る | ||
| https://github.com/colorbox/leetcode/pull/32/changes/BASE..9e158c529cc75864b1ecad429cecfbe15e0723a0#r1898178545 | ||
| > stack に追加する前に範囲チェックをするのも一つです。問題によっては計算量が変わることもあります。そうすると、範囲チェックをして追加という、同じ処理が繰り返されるので関数化をしたりラムダにしたりするのがいいでしょう。 | ||
|
|
||
| - visitedをsetではなく, m * nの配列にするのも良さそう. `visited[i][j] = True`のような使い方 | ||
|
|
||
| - 再帰DFSの解法の別の書き方 | ||
| ```py | ||
| def calculate_area_of_island(x, y): | ||
| if not (0 <= x < m and 0 <= y < n): | ||
| return 0 | ||
|
|
||
| if grid[x][y] == WATER: | ||
| return 0 | ||
|
|
||
| if visited[x][y]: | ||
| return 0 | ||
|
|
||
| visited[x][y] = True | ||
| area = 1 # (x, y)の面積分 | ||
| for dx, dy in directions: | ||
| area += calculate_area_of_island(x + dx, y + dy) | ||
|
|
||
| return area | ||
| ``` | ||
|
|
||
| ## 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. 同じロジックでも色々書き方はあると思います。以下ご参考までに。 import copy
import collections
class Solution:
def maxAreaOfIsland(self, grid: list[list[int]]) -> int:
grid = copy.deepcopy(grid)
num_rows = len(grid)
num_cols = len(grid[0])
WATER = 0
LAND = 1
def is_land(row, col):
return (
0 <= row < num_rows
and 0 <= col < num_cols
and grid[row][col] == LAND
)
def traverse(row_start, col_start):
frontiers = collections.deque()
def push_if_land(row, col):
if is_land(row, col):
frontiers.append((row, col))
grid[row][col] = WATER
push_if_land(row_start, col_start)
area = 0
while frontiers:
row, col = frontiers.popleft()
area += 1
push_if_land(row + 1, col)
push_if_land(row - 1, col)
push_if_land(row, col + 1)
push_if_land(row, col - 1)
return area
max_area = 0
for row in range(num_rows):
for col in range(num_cols):
if is_land(row, col):
area = traverse(row, col)
max_area = max(max_area, area)
return max_area |
||
| ```py | ||
| class Solution: | ||
| def maxAreaOfIsland(self, grid: List[List[int]]) -> int: | ||
| if not grid: | ||
| return 0 | ||
|
|
||
| m = len(grid) | ||
| n = len(grid[0]) | ||
| LAND = 1 | ||
| WATER = 0 | ||
| visited = set() | ||
| directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] | ||
| def calculate_area_of_island(x, y): | ||
| stack = [(x, y)] | ||
| visited.add((x, y)) | ||
| area = 1 | ||
| while stack: | ||
| x, y = stack.pop() | ||
| for dx, dy in directions: | ||
| next_x = x + dx | ||
| next_y = y + dy | ||
| if (next_x, next_y) in visited: | ||
| continue | ||
|
|
||
| if not (0 <= next_x < m and 0 <= next_y < n): | ||
| continue | ||
|
|
||
| if grid[next_x][next_y] == WATER: | ||
| continue | ||
|
|
||
| area += 1 | ||
| stack.append((next_x, next_y)) | ||
| visited.add((next_x, next_y)) | ||
|
|
||
| return area | ||
|
|
||
| max_area = 0 | ||
| for i in range(m): | ||
| for j in range(n): | ||
| if grid[i][j] == LAND: | ||
| if (i, j) in visited: | ||
| continue | ||
|
|
||
| area = calculate_area_of_island(i, j) | ||
| max_area = max(max_area, area) | ||
|
|
||
| return max_area | ||
| ``` | ||
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.
m,nだと変数の中身がわからなくなるので,num_row,num_colなどにする方が好みです.