스택 & 큐

  스택 (Stack) 큐 (Queue)
구조 LIFO (Last In, First Out) FIFO (First In, First Out)
시간복잡도 push() → O(1), pop() → O(1) enqueue() → O(1), dequeue() → O(1)
사용 되돌리기(Undo), 백트래킹, 재귀 호출 대체 처리 순서가 중요한 경우, 탐색, 스케줄링
주요 알고리즘 Monotonic Stack, 백트래킹, DFS 슬라이딩 윈도우, BFS , 위상 정렬

스택 예제

Monotonic Stack: O(N)

  • 다음 큰 수 찾기 → 주식 가격 상승 분석, 온도 변화 예측
  • 히스토그램에서 가장 큰 직사각형

예제 - 다음 큰 수 찾기

🚫 기존 방식 (브루트포스 O(N²))

  • 모든 요소에 대해 오른쪽 숫자들과 비교해서 큰 수를 찾으면 O(N²) 걸림.
public int[] nextGreaterElement(int[] nums) {
    int[] result = new int[nums.length];
    Arrays.fill(result, -1);

    for (int i = 0; i < nums.length; i++) {
        for (int j = i + 1; j < nums.length; j++) {
            if (nums[j] > nums[i]) {
                result[i] = nums[j];
                break;
            }
        }
    }
    return result;
}

문제점

  1. 모든 숫자 쌍을 비교해야 하므로 느림 O(N²)
  2. 데이터가 커질수록 연산량이 많음
public int[] nextGreaterElement(int[] nums) {
    Stack<Integer> stack = new Stack<>();
    int[] result = new int[nums.length];
    Arrays.fill(result, -1);

    for (int i = 0; i < nums.length; i++) {
        while (!stack.isEmpty() && nums[stack.peek()] < nums[i]) {
            result[stack.pop()] = nums[i];
        }
        stack.push(i);
    }
    return result;
}

👉 스택을 활용하면

  1. 한 번만 스캔 (push & pop: O(N))
  2. 불필요한 비교를 제거

큐 예제

Sliding Window Maximum: O(N)

  • CPU 부하 측정, 실시간 온도 변화 감지, 주식 가격 분석

코드 예제 - 슬라이딩 윈도우 최댓값 찾기

🚫 기존 방식 (브루트포스 O(N))

  • 슬라이딩 윈도우 범위 내에서 최대값을 찾기 위해 매번 max()를 호출
public int[] maxSlidingWindow(int[] nums, int k) {
    int[] result = new int[nums.length - k + 1];

    for (int i = 0; i <= nums.length - k; i++) {
        int max = Integer.MIN_VALUE;
        for (int j = i; j < i + k; j++) {
            max = Math.max(max, nums[j]);
        }
        result[i] = max;
    }
    return result;
}

문제점

  1. 매 윈도우마다 max()를 다시 계산 O(NK)
  2. 윈도우 크기가 크면 연산량이 많음
public int[] maxSlidingWindow(int[] nums, int k) {
    Deque<Integer> deque = new LinkedList<>();
    int[] result = new int[nums.length - k + 1];
    int idx = 0;

    for (int i = 0; i < nums.length; i++) {
        while (!deque.isEmpty() && deque.peekFirst() < i - k + 1) {
            deque.pollFirst(); // 범위를 벗어난 값 제거
        }
        while (!deque.isEmpty() && nums[deque.peekLast()] <= nums[i]) {
            deque.pollLast(); // 현재 값보다 작은 값 제거
        }
        deque.addLast(i);
        if (i >= k - 1) result[idx++] = nums[deque.peekFirst()];
    }
    return result;
}

👉 큐를 활용하면

  1. 한 번만 스캔 (O(N))
  2. 불필요한 비교 제거
  3. 윈도우가 이동할 때마다 새로 정렬 x → 시간 절약

https://upcurvewave.tistory.com/724

https://velog.io/@corone_hi/75.-Sliding-Window-Maximum