본문 바로가기
Algorithm(코딩테스트)/코딩테스트 | 기초 개념 시리즈

[Stack 응용] Monotonic Stack

by 카랑현석 2025. 3. 22.

두 줄 요약

각 element를 오름 차순, 내림 차순으로 유지하는 알고리즘 기법

 

배열에서 다음보다(순서 중요) 큰 element나 작은 element를 찾는 문제를 O(n) 이라는 시간에 효율적으로 해결할 수 있다.

 

즉,

1) 배열의 순서가 중요하고 (그 순서를 기점으로 계산해야 하기 때문에)

2) 어떤 기점에서 크거나 작은 요소를 찾아야 하는 문제

3) O(n^2)으로 해결할 수 없고, O(n)으로 해결해야 하는 경우

 

1) && 2) && 3) 의 해당하는 경우 Monotonic Stack을 사용하면 된다.

온도 문제로 생각해보는 Monotonic Stack의 필요성

각 일마다 온도를 순서대로 나타내어 temperatures 배열에 아래와 같이 저장했다고 하자.

temperatures = [73,74,75,71,69,72,76,73]

 

이제 각각의 날을 기준으로 몇 일 후에 따뜻해지는 날이 오는지 구하고 싶다.

예를 들면 첫 날에는 73도이고, 그 날보다 따뜻해지는 날은 바로 다음 날(74도) 이므로 1을 출력하고 싶다.

이때 어느 날도 당일 이후로 따뜻해지는 날이 없다면 0을 출력하고 싶다.

answer = [1,1,4,2,1,1,0,0]

 

이 문제를 해결하려면 모든 배열의 요소를 한 번씩 순회해보면서 답을 찾을 수 있다.

예를 들어 73도 일 때, 다음 날의 온도를 확인해서 73도 보다 크면 74도인 날 - 73도인 날을 구하면 된다.

이런식으로 N개에 대해서 약 N번 순회하면 O(n^2)의 시간복잡도로 답을 구할 수 있다.

 

그런데 만약에 주어진 temperatures의 데이터 값이 10^5 개 이고 제한 시간이 1초라면 O(n^2)으로 해결할 수 있을까?

보통 컴퓨터는 10^8을 연산하는데 1초가 걸리므로 시간 초과가 날 것이다.

 

그러면 O(nlogn) 이나 O(n)으로 해결해야 한다.

O(nlogn)은 정렬 알고리즘인데, 정렬을 해버리면 각 날의 각 데이터 온도 정보가 소실되어 버린다.

첫째 날의 온도가 73도가 아니라 정렬을 하면 첫째 날은 69도가 되어버리기 때문이다.

 

이때 O(n)으로 해결하는 방법으로 Monotonic Stack을 사용할 수 있다.

 

Monotonic Stack

스택에 저장되는 것을 오름차순 / 내림차순 순으로 유지되도록 한다.

 

예시를 들기 위해 내림차순으로 유지되도록 해보자.

1) 만약 스택이 비지 않았다면, (스택의 peek()로 본 그 날의 온도 < 현재 날의 온도) 이면 반복하여 pop을 한다. 그리고 해당 날부터 시작해서 최초로 기온이 해당 날보다 높은 날을 발견했으므로 answer 배열에 정답을 작성한다.

2) 순회하면서 그 날을 push한다.

 

 

 

 

위 이미지와 같이 온도가 75인 둘쨋날은 스택의 peek()인 첫째 날보다 온도가 더 높다. 따라서 pop()을 진행한다.

그리고 현재 day와 스택에 저장된 peek()을 빼면 day가 1인 날 이후로부터 최초로 기온이 높아지는 시점을 구한다. (day-stack.peek())

 

마찬가지로 0일차(73도)의 온도도 2일차(75도)의 온도보다 작으므로 pop()을 하고 answer 배열에 0일차 이후로 최초로 73도보다 높아지는 시점을 answer 배열에 기록한다. (day - stack.peek())

그리고 스택이 비었으므로 현재 날을 push 한다.

 

75보다 71이 더 작으므로 push() 한다.

 

마찬가지로 71보다 69가 더 작으므로 push() 한다.

 

이 과정을 코드로 작성하면 아래 코드와 같다.

import java.util.*;

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int[] answer = new int[temperatures.length];
        Deque<Integer> s = new ArrayDeque<>(); // Monotonic Stack
        
        for(int day=0; day<temperatures.length; day++) {
            while(!s.isEmpty() && temperatures[s.peek()] < temperatures[day]) { // 이전의 온도보다 현재 온도가 더 큰 경우 -> 각각의 해당 지점에서 더 큰 온도인 시점을 찾은 경우
                answer[s.peek()] = day - s.peek();
                s.pop();
            }
            s.push(day);
        }
        
        return answer;
    }
}