Stack
데이터를 후입선출(LIFO, Last In First Out) 순서로 삽입·삭제하는 선형 자료구조입니다.
핵심 개념
| 항목 | 내용 |
| 원리 | 가장 나중에 들어온 데이터가 가장 먼저 나감 |
| 삽입 | push — 스택의 맨 위(top)에 추가 |
| 삭제 | pop — 스택의 맨 위(top)에서 제거 |
| 조회 | peek — 맨 위 원소를 제거하지 않고 확인 |
| 시간 복잡도 | push, pop, peek 모두 O(1) |
push(1) → push(2) → push(3)
│ 3 │ ← top (가장 나중에 삽입)
│ 2 │
│ 1 │ ← bottom (가장 먼저 삽입)
└───┘
pop() → 3 반환 (가장 나중에 들어온 것이 먼저 나감)
│ 2 │ ← top
│ 1 │
└───┘
주요 연산
| 연산 | 설명 | 시간 복잡도 |
push(item) | top에 원소 삽입 | O(1) |
pop() | top 원소 제거 후 반환 | O(1) |
peek() / top() | top 원소 확인 (제거 안 함) | O(1) |
isEmpty() | 스택이 비었는지 확인 | O(1) |
size() | 원소 개수 반환 | O(1) |
Java 구현
1. Java 내장 클래스
// Stack 클래스 (레거시, Vector 기반 — 동기화 오버헤드)
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);
stack.pop(); // 3
stack.peek(); // 2
stack.isEmpty(); // false
stack.size(); // 2
// Deque 사용 권장 (Java 공식 문서 권장 방식)
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1);
stack.push(2);
stack.push(3);
stack.pop(); // 3
stack.peek(); // 2
| 클래스 | 특징 | 권장 여부 |
Stack<E> | Vector 기반, 동기화됨 | X 레거시 |
ArrayDeque<E> | 배열 기반, 동기화 없음, 빠름 | O 권장 |
LinkedList<E> | 연결 리스트 기반, Deque 구현 | △ 가능하지만 ArrayDeque가 더 빠름 |
2. 배열 기반 직접 구현
public class ArrayStack<E> {
private Object[] arr;
private int top;
private int capacity;
public ArrayStack(int capacity) {
this.capacity = capacity;
this.arr = new Object[capacity];
this.top = -1;
}
public void push(E item) {
if (top == capacity - 1) throw new StackOverflowError();
arr[++top] = item;
}
@SuppressWarnings("unchecked")
public E pop() {
if (top == -1) throw new EmptyStackException();
E item = (E) arr[top];
arr[top--] = null;
return item;
}
@SuppressWarnings("unchecked")
public E peek() {
if (top == -1) throw new EmptyStackException();
return (E) arr[top];
}
public boolean isEmpty() { return top == -1; }
public int size() { return top + 1; }
}
3. 연결 리스트 기반 직접 구현
public class LinkedStack<E> {
private static class Node<E> {
E data;
Node<E> next;
Node(E data, Node<E> next) {
this.data = data;
this.next = next;
}
}
private Node<E> top;
private int size;
public void push(E item) {
top = new Node<>(item, top);
size++;
}
public E pop() {
if (top == null) throw new EmptyStackException();
E item = top.data;
top = top.next;
size--;
return item;
}
public E peek() {
if (top == null) throw new EmptyStackException();
return top.data;
}
public boolean isEmpty() { return top == null; }
public int size() { return size; }
}
배열 기반 vs 연결 리스트 기반
| 항목 | 배열 기반 | 연결 리스트 기반 |
| 메모리 | 연속 메모리, 크기 고정 (또는 동적 확장) | 비연속 메모리, 크기 제한 없음 |
| 캐시 효율 | 높음 (메모리 연속) | 낮음 (노드가 흩어짐) |
| push/pop | O(1) (amortized, 동적 확장 시) | O(1) |
| 오버헤드 | 미사용 공간 낭비 가능 | 노드마다 포인터 추가 메모리 |
스택의 활용 사례
| 활용 | 설명 |
| 함수 호출 스택 (Call Stack) | 함수 호출·복귀 주소를 LIFO로 관리 |
| 괄호 검사 | (, {, [ 열린 괄호를 push, 닫힌 괄호와 매칭 |
| 후위 표기식 계산 | 피연산자를 push, 연산자 만나면 pop하여 계산 |
| DFS (깊이 우선 탐색) | 방문할 노드를 스택에 저장 |
| Undo/Redo | 작업 이력을 스택으로 관리 |
| 브라우저 뒤로 가기 | 방문 기록을 스택에 저장 |
| JVM 스택 프레임 | 메서드 호출마다 스택 프레임 push/pop |
함수 호출 스택 예시
public static void main(String[] args) { // ① main 프레임 push
a(); // ② a 프레임 push
}
static void a() {
b(); // ③ b 프레임 push
}
static void b() {
System.out.println("hello"); // ④ println 프레임 push
} // ⑤ println → b → a → main 순서로 pop
호출 스택 변화:
① │ main │ ② │ a │ ③ │ b │ ④ │println│
└──────┘ │ main │ │ a │ │ b │
└──────┘ │ main │ │ a │
└──────┘ │ main │
└──────┘
→ 복귀: println pop → b pop → a pop → main pop
괄호 검사 예시
public static boolean isValid(String s) {
Deque<Character> stack = new ArrayDeque<>();
for (char c : s.toCharArray()) {
if (c == '(' || c == '{' || c == '[') {
stack.push(c);
} else {
if (stack.isEmpty()) return false;
char top = stack.pop();
if (c == ')' && top != '(') return false;
if (c == '}' && top != '{') return false;
if (c == ']' && top != '[') return false;
}
}
return stack.isEmpty();
}
isValid("({[]})"); // true
isValid("({[}])"); // false
"({[]})" 검사 과정:
( → push( │ ( │
{ → push{ │ { │
│ ( │
[ → push[ │ [ │
│ { │
│ ( │
] → pop[ O │ { │
│ ( │
} → pop{ O │ ( │
) → pop( O └───┘ 비었으므로 O valid
| 항목 | 스택 (Stack) | 큐 (Queue) |
| 원리 | LIFO (후입선출) | FIFO (선입선출) |
| 삽입 | push (top에) | enqueue (rear에) |
| 삭제 | pop (top에서) | dequeue (front에서) |
| 비유 | 접시 쌓기 | 줄 서기 |
| 활용 | 함수 호출, DFS, Undo | BFS, 작업 큐, 버퍼 |
[스택 — LIFO] [큐 — FIFO]
│ 3 │ ← pop front → │ 1 │ 2 │ 3 │ ← rear
│ 2 │ dequeue enqueue
│ 1 │
└───┘