문제 링크 : https://leetcode.com/problems/making-a-large-island/
Making A Large Island - LeetCode
Level up your coding skills and quickly land a job. This is the best place to expand your knowledge and get prepared for your next interview.
leetcode.com
문제 해설
n x n 크기의 행렬이 주어집니다. 단 한 번 행렬의 요소 0을 1로 바꿀 수 있습니다.
이 과정을 적용해서 얻을 수 있는 가장 큰 섬(island)의 크기를 반환하세요.
섬은 4방향으로 이어진 1로 이루어진 집합입니다.
제한 사항
n == grid.length
n == grid[i].length
1 <= n <= 500
grid[i][j] is either 0 or 1
난이도 Hard
접근 방법
처음 떠올린 방법은 bfs로 섬의 크기를 구하는 함수를 작성하고
행렬 속 모든 0에 대해서 1로 바꾸었을 때 섬의 크기를 각각 구한 뒤 비교하여 최댓값을 얻는 방법을 떠올렸습니다.
1) 그런데 최초로 섬의 크기를 구할 경우 O(N^2)만큼 시간이 소요되고
2) 다시 행렬을 재탐색하며 모든 0에 대해서 1로 치환했을 때마다 섬의 크기를 다시 계산하므로
최악의 경우 O(N^4) 만큼 시간이 소요됩니다.
이 경우 시간 초과 판정을 받을 수 있기 때문에 다른 방법을 모색해야 했습니다.
최초의 상황에서 섬의 크기를 구하고 0을 1로 바꿨을 때 합쳐지는 섬을 따로 구분할 수 있다면
2)의 과정에서 소요되는 시간을 크게 줄일 수 있다는 아이디어가 떠올랐습니다.
def search_island(self, grid: List[List[int]], r: int, c: int, num: int) -> int:
dRow = [0, 0, 1, -1]
dCol = [1, -1, 0, 0]
n = len(grid)
queue = deque()
queue.append((r, c))
grid[r][c] = num
SIZE = 1
while queue:
row, col = queue.popleft()
for d in range(4):
newR = row + dRow[d]
newC = col + dCol[d]
if newR < 0 or newC < 0 or newR >= n or newC >= n:
continue
elif grid[newR][newC] != 1:
continue
grid[newR][newC] = num
queue.append((newR, newC))
SIZE += 1
return SIZE
search_island 함수는 r행 c열부터 시작해서 섬을 탐색하고 그 크기를 반환하는 함수입니다.
행렬 정보를 담은 grid와 위치 정보를 담은 r,c 그리고 섬 번호를 담은 num을 매개변수로 받습니다.
bfs를 이용해서 4방향 탐색을 실행합니다.
이 때, 방문처리를 위한 visited 리스트를 사용하지 않고 주어진 grid를 수정해서 방문 처리를 실시합니다.
(섬 정보를 저장하기 위한 새 리스트를 생성하지 않으므로 메모리를 절약할 수 있습니다.)
grid의 값이 0이면 빈 곳, 1이면 아직 탐색하지 않은 땅을 의미하고
grid의 값 n이 2 이상이면 탐색한 땅이며 섬 번호 n에 속함을 의미합니다.
def largestIsland(self, grid: List[List[int]]) -> int:
n = len(grid)
sizes = [0, 0]
num = 2
for r in range(n):
for c in range(n):
if grid[r][c] != 1:
continue
size = self.search_island(grid, r, c, num)
sizes.append(size)
num += 1
max_size = max(sizes)
dRow = [0, 0, 1, -1]
dCol = [1, -1, 0, 0]
for r in range(n):
for c in range(n):
islands_to_merge = set()
if grid[r][c] != 0:
continue
for d in range(4):
newR = r + dRow[d]
newC = c + dCol[d]
if newR < 0 or newC < 0 or newR >= n or newC >= n:
continue
elif grid[newR][newC] == 0:
continue
islands_to_merge.add(grid[newR][newC])
size = 1
for island in islands_to_merge:
size += sizes[island]
max_size = max(max_size, size)
return max_size
sizes 리스트는 각 섬의 크기 정보를 저장하기 위한 리스트입니다.
이 때 섬 정보 조회를 용이하게 하기 위해 사용될 일 없는 0번 인덱스와 1번 인덱스는 임의로 0으로 채워넣었습니다.
(grid에서 0은 빈 땅, 1은 탐색하지 않은 땅을 의미하므로 크기를 조회할 일이 없습니다.)
섬 번호를 의미하는 num은 2부터 시작하도록 했습니다.
grid를 탐색하며 1을 만나면 search_island 함수를 통해 섬을 방문처리하고 크기를 계산하여 sizes에 추가하도록 했습니다.
max_size는 최초 상황에서 가장 큰 섬의 크기를 저장합니다.
다시 grid를 탐색하며 0을 1로 바꿨을 때 합쳐지는 섬의 크기를 구했습니다.
(이전 과정을 통해 grid에 요소 1은 남아있지 않습니다.)
탐색 중에 0을 만나면 4방향으로 탐색하여 서로 다른 섬이 있는지 확인하고 size에 그 크기를 더하고
합쳐진 섬의 크기를 max_size와 비교하여 최댓값으로 갱신했습니다.
결과
from typing import List
from collections import deque
class Solution:
def largestIsland(self, grid: List[List[int]]) -> int:
n = len(grid)
sizes = [0, 0]
num = 2
for r in range(n):
for c in range(n):
if grid[r][c] != 1:
continue
size = self.search_island(grid, r, c, num)
sizes.append(size)
num += 1
max_size = max(sizes)
dRow = [0, 0, 1, -1]
dCol = [1, -1, 0, 0]
for r in range(n):
for c in range(n):
islands_to_merge = set()
if grid[r][c] != 0:
continue
for d in range(4):
newR = r + dRow[d]
newC = c + dCol[d]
if newR < 0 or newC < 0 or newR >= n or newC >= n:
continue
elif grid[newR][newC] == 0:
continue
islands_to_merge.add(grid[newR][newC])
size = 1
for island in islands_to_merge:
size += sizes[island]
max_size = max(max_size, size)
return max_size
def search_island(self, grid: List[List[int]], r: int, c: int, num: int) -> int:
dRow = [0, 0, 1, -1]
dCol = [1, -1, 0, 0]
n = len(grid)
queue = deque()
queue.append((r, c))
grid[r][c] = num
SIZE = 1
while queue:
row, col = queue.popleft()
for d in range(4):
newR = row + dRow[d]
newC = col + dCol[d]
if newR < 0 or newC < 0 or newR >= n or newC >= n:
continue
elif grid[newR][newC] != 1:
continue
grid[newR][newC] = num
queue.append((newR, newC))
SIZE += 1
return SIZE
평가
runtime과 memory 사용량에서 매우 효율적인 코드임이 확인되었습니다.
Hard 난이도였는데 깔끔하고 좋은 코드로 빠르게 통과해서 정말 기분이 좋았습니다 !
'Problems > Leetcode' 카테고리의 다른 글
Bitwise AND of Numbers Range (python) (0) | 2021.11.15 |
---|---|
Longest Increasing Subsequence (python) 풀이 (0) | 2021.11.11 |
3Sum Closest (0) | 2021.07.27 |
Convert Sorted Array to Binary Search Tree (0) | 2021.07.27 |
Binary Tree Pruning (0) | 2021.07.24 |