스택 2개로 큐를, 큐 2개로 스택을 만드는 방법과, 그 시간복잡도에 대해 설명해 주세요.
두 자료구조의 순서 방향이 반대임을 이용해, 한 자료구조를 두 번 뒤집어 다른 자료구조를 구현하는 방식입니다.
스택 2개로 큐 구현
inbox 스택에 넣고, outbox 스택으로 옮겨 순서를 역전시킵니다.
enqueue(1,2,3): dequeue():
inbox inbox → outbox로 이동
[3] ← top outbox
[2] [1] ← top (순서 역전!)
[1] [2]
[3]
→ poll() : 1 반환 O
class QueueUsingStacks {
Deque<Integer> inbox = new ArrayDeque<>(); // 입력용
Deque<Integer> outbox = new ArrayDeque<>(); // 출력용
void enqueue(int data) {
inbox.push(data); // O(1)
}
int dequeue() {
if (outbox.isEmpty()) {
while (!inbox.isEmpty())
outbox.push(inbox.pop()); // inbox → outbox 이동
}
return outbox.pop();
}
}
시간복잡도
| 연산 | 복잡도 | 이유 |
|---|---|---|
| enqueue | O(1) | inbox에 바로 push |
| dequeue | O(1) 분할상환 | 각 요소는 평생 1번씩만 이동 |
| dequeue 최악 | O(n) | outbox 비었을 때 전체 이동 |
분할상환(Amortized): n번 연산의 총 비용이 O(n)이므로 평균 O(1)
큐 2개로 스택 구현
매 pop 시 요소를 다른 큐로 옮겨 마지막 요소를 꺼냅니다.
push(1,2,3): pop():
q1 q1 → q2로 (n-1)개 이동
[1, 2, 3] q1: [3] ← 마지막 요소 반환
q2: [1, 2]
이후 q1, q2 swap
class StackUsingQueues {
Queue<Integer> q1 = new LinkedList<>();
Queue<Integer> q2 = new LinkedList<>();
void push(int data) {
q1.offer(data); // O(1)
}
int pop() { // O(n)
// q1의 마지막 요소만 남기고 q2로 이동
while (q1.size() > 1)
q2.offer(q1.poll());
int result = q1.poll(); // 마지막 요소 = top
// q1, q2 swap
Queue<Integer> temp = q1;
q1 = q2;
q2 = temp;
return result;
}
}
시간복잡도
| 연산 | 복잡도 | 이유 |
|---|---|---|
| push | O(1) | 큐에 바로 삽입 |
| pop | O(n) | 매번 n-1개 요소 이동 |
최종 비교
| 구현 | push/enqueue | pop/dequeue |
|---|---|---|
| 스택 2개 → 큐 | O(1) | O(1) 분할상환 |
| 큐 2개 → 스택 | O(1) | O(n) |
큐 2개로 스택을 만들면 pop이 항상 O(n)으로, 효율적인 구현이 어렵습니다. 반면 스택 2개로 큐를 만들면 분할상환 O(1)로 실용적인 구현이 가능합니다.