LeetCode 739. Daily Temperatures

📊 결과


💻 내 코드

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;
    }
}

📝 평가

✔ 잘한 점

  1. 단조 스택 패턴 적용: 핵심 알고리즘을 정확히 파악
  2. 인덱스 저장: 온도가 아닌 인덱스를 스택에 저장하여 거리 계산 가능
  3. while 루프 활용: 현재 온도보다 낮은 모든 이전 온도 처리
  4. 거리 계산: i - index로 정확한 일수 계산
  5. 정답 도출: 모든 테스트 케이스 통과

✦ 개선점

  1. 불필요한 초기화: deque.addFirst(0) → 루프 내에서 처리 가능

  2. 불필요한 마지막 처리:

     if(i==temperatures.length-1 && !deque.isEmpty()){
         while(!deque.isEmpty()){
             int index = deque.removeLast();
             answer[index]=0;
         }
     }
    
    • answer 배열은 이미 0으로 초기화되어 있음!
    • 명시적으로 0을 설정할 필요 없음
  3. 메소드 혼용: addFirst + addLast + peekLast + removeLast

    • Stack처럼 한쪽만 사용하는 게 더 명확함
  4. 변수명: dequestack이 의미상 더 정확


✨ 최적화된 풀이

방법 1: 깔끔한 단조 스택 (추천 ⭐⭐⭐⭐⭐)

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;
    }
}

개선 포인트:


방법 2: 역방향 순회 (공간 최적화)

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;
    }
}

특징: 뒤에서부터 처리하여 “다음 더 큰 원소” 관점으로 접근


방법 3: 배열 기반 스택 (메모리 최적화)

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;
    }
}

장점:


방법 4: Next Greater Element 패턴 (가독성)

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]


배운 점

  1. 단조 스택(Monotonic Stack)의 정의

     단조 증가 스택: 아래 → 위로 증가
     단조 감소 스택: 아래 → 위로 감소
        
     이 문제: 단조 감소 스택 사용
     (스택 top으로 갈수록 온도가 낮거나 같음)
    
  2. “다음 더 큰 원소” 패턴

     - 배열을 순회하며
     - 현재 원소가 스택의 원소보다 크면
     - 스택에서 pop하며 답 계산
     - 현재 원소를 스택에 push
    
  3. 시간복잡도 O(n) 보장

     각 원소는:
     - 정확히 1번 push
     - 최대 1번 pop
     → 총 연산: 2n = O(n)
    
  4. 배열 초기화의 활용

     int[] answer = new int[n];  // 모든 값이 0
     // 더 따뜻한 날이 없으면 자동으로 0!
    

핵심 개념

단조 스택의 전형적인 사용 사례:

  1. Next Greater Element (다음 더 큰 원소)
  2. Next Smaller Element (다음 더 작은 원소)
  3. Previous Greater Element (이전 더 큰 원소)
  4. Previous Smaller Element (이전 더 작은 원소)

패턴 인식:


🎯 개선 후 코드

추천: 깔끔한 단조 스택 방식

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
 */

개선 효과:


📚 관련 개념

알고리즘 패턴

연관 개념

  1. 단조 스택의 종류

     단조 증가 스택 (Monotonic Increasing)
     예: [1, 3, 5, 7]
     용도: Next/Previous Smaller Element
        
     단조 감소 스택 (Monotonic Decreasing)
     예: [7, 5, 3, 1]
     용도: Next/Previous Greater Element
    
  2. 실무 활용

    • 주식 가격 분석: 다음으로 오르는/내리는 날 찾기
    • 히스토그램: 최대 직사각형 넓이
    • 빌딩 가시성: 더 높은 빌딩까지의 거리
    • 날씨 예측: 온도/습도 변화 추적
  3. 관련 자료구조

    • Deque (양방향 큐)
    • Priority Queue (우선순위 큐)
    • Segment Tree (구간 쿼리)

🎓 다음 단계

비슷한 문제

  1. LeetCode 496. Next Greater Element I ⭐⭐

    • NGE의 기초 문제
  2. LeetCode 503. Next Greater Element II ⭐⭐

    • 원형 배열에서 NGE (순환)
  3. LeetCode 901. Online Stock Span ⭐⭐

    • 주식 스팬 계산 (이전보다 작거나 같은 날 수)
  4. LeetCode 84. Largest Rectangle in Histogram ⭐⭐⭐⭐⭐

    • 히스토그램 최대 직사각형 (단조 스택 고급)
  5. LeetCode 42. Trapping Rain Water ⭐⭐⭐⭐

    • 빗물 가두기 (단조 스택 응용)
  6. LeetCode 85. Maximal Rectangle ⭐⭐⭐⭐⭐

    • 2D 히스토그램 문제

유사 패턴 문제

  1. LeetCode 907. Sum of Subarray Minimums ⭐⭐⭐
  2. LeetCode 1019. Next Greater Node In Linked List ⭐⭐
  3. LeetCode 1475. Final Prices With a Special Discount

연습 포인트 체크리스트


🔍 단조 스택 마스터하기

패턴 템플릿

// 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²)

최적화 사고:

  1. “각 날짜마다 뒤를 모두 확인? → 중복 탐색 많음”
  2. “이미 확인한 정보를 재사용할 수 없을까?”
  3. “스택으로 미해결 날짜들을 추적하자!”
  4. “더 따뜻한 날을 만나면 한 번에 여러 날 해결!”

단조 스택의 직관:

“아직 답을 찾지 못한 날들을 스택에 쌓아두고, 답이 될 수 있는 날을 만나면 한꺼번에 해결!”


🏷️ Keywords

#MonotonicStack #Stack #NextGreaterElement #NGE #단조스택 #Deque #TimeSeriesAnalysis
#SlidingWindow #온도분석 #ArrayTraversal #O(n)Algorithm #SpaceOptimization
#LeetCodeMedium #코딩테스트 #스택활용 #패턴인식