[LeetCode] 42. Trapping Rain Water
1ilsang
클라이밍 하실래염?
Published
빗물이 고일 수 있는 모든 영역을 구하면 되는 문제다.
기본적으로 물은 '높은 곳에서 낮은 곳'으로 흐르기 때문에 우리는 높이를 비교하면서 빗물이 고일 수 있는지 판단해야 한다.
이 문제는 두 가지 스택과 투포인터 두 가지로 풀 수 있다. 두 방법 모두 알아두면 좋기 때문에 두 가지 해석을 모두 하려고 한다.
접근법 1. Stack O(n) T(n)
스택 접근은 "가로로 면적을 합해가는" 방식이다.
빗물이 고이기 위해서는 양쪽으로 벽이 있어야 한다.
스택을 활용해 양 벽을 계산하는 방법은, 높이가 감소할 때는 스택에 푸쉬하고 높이가 이전 탑보다 높아질 때는 팝을 하면서 얼마만큼의 빗물을 저장할 수 있는지 계산하면 된다.
0번째 높이부터 순회해 보자.
- 모든 원소를 순회할 때마다 스택에 푸쉬한다. 왼쪽의 그림과 같이 높이가 감소해 나갈 때에는 특별한 작업 없이 계속 진행한다.
- 중앙의 그림(
i = 3
)의 상황일 경우 현재 벽이 스택의 Top 값보다 더 높기 때문에 빗물이 고일 수 있다. 현재 높이와 같거나 클 때까지 스택에 쌓여있는 벽들을 pop 하며 "가로로" 누적값을 더한다. 여기서는 높이 1이 최대이므로 스택에 추가된 높이 2(i = 0
)까지 계산하지 않고 끝난다. - 우측의 그림(
i = 4
)에서도 동일하다. 현재 높이보다 큰 높이가 나올 때까지 팝을 하며 계산한다. 2번에서 이미 높이 1일 때의 경우를 계산했으므로 높이 2일 때의 가로 값만 계산하면 된다.
마지막 노드까지 위의 방식을 계속해서 하면 모든 면적을 구할 수 있다. 코드와 라인별 해석은 제일 아래에 작성해 두었다.
접근법 2. Two Pointers O(n) T(1)
투포인터 접근은 "세로로 면적을 합해가는" 방식이다.
빗물이 고이기 위해서는 양쪽으로 벽이 있어야 한다.
투포인터는 양끝에 포인터를 설정하고 좌우로 움직이며 높이가 더 높은 쪽을 향해 간다. 높이가 높은 쪽을 향해 양 포인터를 옮기면 반대로 낮은 쪽은 빗물이 고이는 곳이기 때문에 세로로 더해나가면 된다.
L, R을 양 끝에 두고 순회하면서 각각 MAX 값을 구한다.
- 최초의 상태. 최대값(가로선)을 설정한다.
L < R
이라면 L을 옮기고 아니면 R을 옮긴다. 여기는 L를 옮겼다.i = 1
의 높이가 L의 최대높이보다 낮으므로 그 차이를 더한다.L < R
일 때까지 L을 옮긴 모습이다.- R을 옮겼다.
i = 8
의 높이가 R의 최대높이보다 낮으므로 그 차이를 더한다. - 계속해서 반복하면 결국 LR은 한곳으로 모이고 그 사이의 모든 세로 값이 더해져 빗물의 면적을 구할 수 있다.
최종 코드
☕ 소주 한 잔 후원하기
(예금주: 이상철)