자료구조 - Stack / Queue / Deque
Summary
직전 값 비교는 스택, 먼저 들어온 것부터 처리하면 큐, 양쪽을 직접 조작하면 덱이다.
Java 메서드 방향표는문법/A_필수/ArrayDeque.md를 대표 문서로 본다.
언제 쓰는가
| 문제 신호 | 자료구조 | 핵심 |
|---|---|---|
| 최소 이동 횟수, 레벨 순회 | Queue | FIFO, offer/poll |
| 괄호, 인접 제거, 직전 값 비교 | Stack | LIFO, push/pop/peek |
| 오큰수, 이전보다 큰/작은 값 | Monotonic Stack | 인덱스를 저장 |
| 0-1 BFS | Deque | 비용 0은 앞, 비용 1은 뒤 |
| 슬라이딩 윈도우 최댓값/최솟값 | Deque | 후보 인덱스를 단조 유지 |
문제 신호
먼저 들어온 순서대로 처리한다 → Queue
가장 최근에 넣은 것부터 확인한다 → Stack
양쪽에서 넣고 빼야 한다 → Deque
현재 값보다 작거나 큰 이전 값들을 제거한다 → 단조 스택/단조 덱
윈도우가 한 칸씩 이동하며 최댓값/최솟값이 필요하다 → 단조 덱Queue BFS 기본
Queue<Integer> q = new ArrayDeque<>();
q.offer(start);
visited[start] = true;
while (!q.isEmpty()) {
int cur = q.poll();
for (int next : graph[cur]) {
if (visited[next]) continue;
visited[next] = true;
q.offer(next);
}
}| 실수 | 주의 |
|---|---|
push() 사용 | 큐가 아니라 스택 방향이 됨 |
| visited를 poll 때 체크 | 중복 삽입이 많아질 수 있음 |
poll() 결과 언박싱 | 비어 있으면 null. while 조건 유지 |
Stack 기본
Deque<Integer> st = new ArrayDeque<>();
for (int x : arr) {
while (!st.isEmpty() && st.peek() < x) {
st.pop();
}
st.push(x);
}스택 문제는 다음 조합으로 통일한다.
Deque<Integer> st = new ArrayDeque<>();
push / pop / peek단조 스택 - 오큰수 템플릿
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine());
int[] arr = new int[n];
int[] ans = new int[n];
Arrays.fill(ans, -1);
StringTokenizer stInput = new StringTokenizer(br.readLine());
for (int i = 0; i < n; i++) arr[i] = Integer.parseInt(stInput.nextToken());
Deque<Integer> st = new ArrayDeque<>(); // 인덱스 저장
for (int i = 0; i < n; i++) {
while (!st.isEmpty() && arr[st.peek()] < arr[i]) {
ans[st.pop()] = arr[i];
}
st.push(i);
}
StringBuilder sb = new StringBuilder();
for (int x : ans) sb.append(x).append(' ');
System.out.println(sb);
}
}복잡도: 각 인덱스가 한 번 push, 한 번 pop되므로 O(N).
Deque - 슬라이딩 윈도우 최댓값
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer stInput = new StringTokenizer(br.readLine());
int n = Integer.parseInt(stInput.nextToken());
int k = Integer.parseInt(stInput.nextToken());
int[] arr = new int[n];
stInput = new StringTokenizer(br.readLine());
for (int i = 0; i < n; i++) arr[i] = Integer.parseInt(stInput.nextToken());
Deque<Integer> dq = new ArrayDeque<>(); // 후보 인덱스, 값은 내림차순 유지
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
while (!dq.isEmpty() && dq.peekFirst() <= i - k) dq.pollFirst();
while (!dq.isEmpty() && arr[dq.peekLast()] <= arr[i]) dq.pollLast();
dq.offerLast(i);
if (i >= k - 1) sb.append(arr[dq.peekFirst()]).append(' ');
}
System.out.println(sb);
}
}복잡도: 각 인덱스가 한 번 들어가고 한 번 빠지므로 O(N).
자주 하는 실수
| 실수 | 수정 |
|---|---|
큐처럼 쓰면서 push() | offer() 또는 offerLast() 사용 |
스택처럼 쓰면서 offer() | push() 사용 |
| 덱 문제에서 방향 없는 메서드 혼용 | offerFirst/offerLast/pollFirst/pollLast로 명시 |
| 단조 스택에 값 저장 후 답 인덱스 잃음 | 대부분 인덱스를 저장 |
| 슬라이딩 윈도우에서 만료 인덱스 제거 누락 | peekFirst() <= i - k 제거 |
백지 복원 질문
1. BFS 큐의 Java 선언과 세 메서드는?
2. 스택 문제의 Java 선언과 세 메서드는?
3. 단조 스택에서 보통 값이 아니라 인덱스를 저장하는 이유는?
4. 슬라이딩 윈도우 최댓값에서 앞/뒤에서 각각 무엇을 제거하는가?
5. 0-1 BFS에서 비용 0 간선은 어느 쪽에 넣는가?