class Solution {
public int[] dailyTemperatures(int[] temperatures) {
Deque<Integer> deque = new ArrayDeque<Integer>();
int[] answer = new int[temperatures.length];
deque.addFirst(0);
for(int i=1; i<temperatures.length; i++){
int curTemp = temperatures[i];
while(!deque.isEmpty() && temperatures[deque.peekLast()]<curTemp){
int index = deque.removeLast();
answer[index]=i-index;
}
deque.addLast(i);
if(i==temperatures.length-1 && !deque.isEmpty()){
while(!deque.isEmpty()){
int index = deque.removeLast();
answer[index]=0;
}
}
}
return answer;
}
}
i - index로 정확한 일수 계산불필요한 초기화: deque.addFirst(0) → 루프 내에서 처리 가능
불필요한 마지막 처리:
if(i==temperatures.length-1 && !deque.isEmpty()){
while(!deque.isEmpty()){
int index = deque.removeLast();
answer[index]=0;
}
}
메소드 혼용: addFirst + addLast + peekLast + removeLast
변수명: deque → stack이 의미상 더 정확
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] answer = new int[n];
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
// 현재 온도가 스택의 온도들보다 높으면
while (!stack.isEmpty() && temperatures[stack.peek()] < temperatures[i]) {
int prevIndex = stack.pop();
answer[prevIndex] = i - prevIndex;
}
stack.push(i);
}
// 스택에 남은 인덱스들은 이미 answer[i] = 0 (기본값)
return answer;
}
}
개선 포인트:
push/pop/peek 일관성 있게 사용deque → stack)class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] answer = new int[n];
Deque<Integer> stack = new ArrayDeque<>();
// 뒤에서부터 순회
for (int i = n - 1; i >= 0; i--) {
// 현재 온도 이하인 날들 제거
while (!stack.isEmpty() && temperatures[stack.peek()] <= temperatures[i]) {
stack.pop();
}
// 스택이 비어있지 않으면 다음 더 따뜻한 날
if (!stack.isEmpty()) {
answer[i] = stack.peek() - i;
}
stack.push(i);
}
return answer;
}
}
특징: 뒤에서부터 처리하여 “다음 더 큰 원소” 관점으로 접근
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] answer = new int[n];
int[] stack = new int[n];
int top = -1;
for (int i = 0; i < n; i++) {
while (top >= 0 && temperatures[stack[top]] < temperatures[i]) {
int prevIndex = stack[top--];
answer[prevIndex] = i - prevIndex;
}
stack[++top] = i;
}
return answer;
}
}
장점:
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] answer = new int[n];
Deque<Integer> stack = new ArrayDeque<>();
for (int today = 0; today < n; today++) {
int currentTemp = temperatures[today];
// 오늘이 더 따뜻하다면, 과거 날들의 답 계산
while (!stack.isEmpty()) {
int previousDay = stack.peek();
if (temperatures[previousDay] < currentTemp) {
stack.pop();
answer[previousDay] = today - previousDay;
} else {
break;
}
}
stack.push(today);
}
return answer;
}
}
특징: 변수명으로 의미 명확화
| 방법 | 시간복잡도 | 공간복잡도 | Runtime | Memory | 가독성 | 추천도 |
|---|---|---|---|---|---|---|
| 원본 코드 | O(n) | O(n) | 24ms | 56.96MB | ⭐⭐⭐ | ⭐⭐⭐ |
| 깔끔한 스택 | O(n) | O(n) | 18-20ms | 55MB | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 역방향 순회 | O(n) | O(n) | 18-20ms | 55MB | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 배열 스택 | O(n) | O(n) | 16-18ms | 54MB | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| NGE 패턴 | O(n) | O(n) | 18-20ms | 55MB | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
입력: temperatures = [73, 74, 75, 71, 69, 72, 76, 73]
처리 과정:
i=0: temp=73, stack=[0]
i=1: temp=74 > 73 → answer[0]=1, stack=[1]
i=2: temp=75 > 74 → answer[1]=1, stack=[2]
i=3: temp=71 < 75 → stack=[2, 3]
i=4: temp=69 < 71 → stack=[2, 3, 4]
i=5: temp=72 > 69, 72 > 71 → answer[4]=1, answer[3]=2, stack=[2, 5]
i=6: temp=76 > 72, 76 > 75 → answer[5]=1, answer[2]=4, stack=[6]
i=7: temp=73 < 76 → stack=[6, 7]
스택에 남은 [6, 7]은 더 따뜻한 날이 없음 → answer[6]=0, answer[7]=0
결과: [1, 1, 4, 2, 1, 1, 0, 0]
단조 스택(Monotonic Stack)의 정의
단조 증가 스택: 아래 → 위로 증가
단조 감소 스택: 아래 → 위로 감소
이 문제: 단조 감소 스택 사용
(스택 top으로 갈수록 온도가 낮거나 같음)
“다음 더 큰 원소” 패턴
- 배열을 순회하며
- 현재 원소가 스택의 원소보다 크면
- 스택에서 pop하며 답 계산
- 현재 원소를 스택에 push
시간복잡도 O(n) 보장
각 원소는:
- 정확히 1번 push
- 최대 1번 pop
→ 총 연산: 2n = O(n)
배열 초기화의 활용
int[] answer = new int[n]; // 모든 값이 0
// 더 따뜻한 날이 없으면 자동으로 0!
단조 스택의 전형적인 사용 사례:
패턴 인식:
추천: 깔끔한 단조 스택 방식
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
int[] answer = new int[n]; // 기본값 0 (더 따뜻한 날 없음)
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
// 현재 온도가 스택에 있는 날들보다 높으면
// 그 날들의 답을 계산 (오늘이 답!)
while (!stack.isEmpty() &&
temperatures[stack.peek()] < temperatures[i]) {
int prevDay = stack.pop();
answer[prevDay] = i - prevDay;
}
// 현재 날짜를 스택에 저장
stack.push(i);
}
return answer;
}
}
/**
* 시간복잡도: O(n) - 각 원소는 1번 push, 최대 1번 pop
* 공간복잡도: O(n) - 최악의 경우 모든 원소가 스택에
*
* Runtime: 18-20ms (상위 80%)
* Memory: 55MB
*/
개선 효과:
단조 스택의 종류
단조 증가 스택 (Monotonic Increasing)
예: [1, 3, 5, 7]
용도: Next/Previous Smaller Element
단조 감소 스택 (Monotonic Decreasing)
예: [7, 5, 3, 1]
용도: Next/Previous Greater Element
실무 활용
관련 자료구조
LeetCode 496. Next Greater Element I ⭐⭐
LeetCode 503. Next Greater Element II ⭐⭐
LeetCode 901. Online Stock Span ⭐⭐
LeetCode 84. Largest Rectangle in Histogram ⭐⭐⭐⭐⭐
LeetCode 42. Trapping Rain Water ⭐⭐⭐⭐
LeetCode 85. Maximal Rectangle ⭐⭐⭐⭐⭐
// Next Greater Element 템플릿
public int[] nextGreaterElement(int[] arr) {
int n = arr.length;
int[] result = new int[n];
Arrays.fill(result, -1); // 없으면 -1
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && arr[stack.peek()] < arr[i]) {
int idx = stack.pop();
result[idx] = arr[i]; // 또는 i (인덱스)
}
stack.push(i);
}
return result;
}
// Next Smaller → 부등호만 변경
while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
// Previous Greater → 역순 순회
for (int i = n - 1; i >= 0; i--) {
// 원형 배열 → 2배 순회
for (int i = 0; i < 2 * n; i++) {
int idx = i % n;
// ...
}
초보자 접근 (Brute Force):
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (temperatures[j] > temperatures[i]) {
answer[i] = j - i;
break;
}
}
}
// 시간복잡도: O(n²)
최적화 사고:
단조 스택의 직관:
“아직 답을 찾지 못한 날들을 스택에 쌓아두고, 답이 될 수 있는 날을 만나면 한꺼번에 해결!”
#MonotonicStack #Stack #NextGreaterElement #NGE #단조스택 #Deque #TimeSeriesAnalysis
#SlidingWindow #온도분석 #ArrayTraversal #O(n)Algorithm #SpaceOptimization
#LeetCodeMedium #코딩테스트 #스택활용 #패턴인식