🔎 스택이란?
스택은 자료를 차곡차곡 쌓아 올리는 구조!
→ 가장 마지막에 넣은 데이터가 제일 먼저 나감 (LIFO: Last In, First Out)
🔥 자바에서 스택 사용하는 법
자바에서는 스택을 클래스로 제공합니다.
따라서 해당 라이브러리를 import 해주면 됩니다.
import java.util.Stack;
주요 메서드
| 메서드 이름 | 설명 | 예시 |
| push(item) | 스택에 아이템을 추가한다. | stack.psh(10); |
| pop() | 스택의 맨 위 아이템을 꺼내고 제거한다. | int top = stack.pop(); |
| peek() | 스택의 맨 위 아이템을 꺼내지만 제거하지 않는다. | int top = stack.peek(); |
| isEmpty() | 스택이 비어있는지 확인한다. (true of false) | stack.isEmpty(); |
| size() | 스택 안에 몇 개의 item이 들어있는지 알려준다. | stack.size(); |
문제
정수를 저장하는 스택을 구현한 다음, 입력으로 주어지는 명령을 처리하는 프로그램을 작성하시오.
명령은 총 다섯 가지이다.
- push X: 정수 X를 스택에 넣는 연산이다.
- pop: 스택에서 가장 위에 있는 정수를 빼고, 그 수를 출력한다. 만약 스택에 들어있는 정수가 없는 경우에는 -1을 출력한다.
- size: 스택에 들어있는 정수의 개수를 출력한다.
- empty: 스택이 비어있으면 1, 아니면 0을 출력한다.
- top: 스택의 가장 위에 있는 정수를 출력한다. 만약 스택에 들어있는 정수가 없는 경우에는 -1을 출력한다.
입력
첫째 줄에 주어지는 명령의 수 N (1 ≤ N ≤ 10,000)이 주어진다. 둘째 줄부터 N개의 줄에는 명령이 하나씩 주어진다. 주어지는 정수는 1보다 크거나 같고, 100,000보다 작거나 같다. 문제에 나와있지 않은 명령이 주어지는 경우는 없다.
출력
출력해야하는 명령이 주어질 때마다, 한 줄에 하나씩 출력한다.
자바 코드
import java.util.Scanner;
import java.util.Stack;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Stack<Integer> stack = new Stack<>();
int n = sc.nextInt();
sc.nextLine(); // 개행 문자 제거
for (int i = 0; i < n; i++) {
String command = sc.nextLine();
if (command.startsWith("push")) {
int num = Integer.parseInt(command.split(" ")[1]);
stack.push(num);
} else if (command.equals("pop")) {
System.out.println(stack.isEmpty() ? -1 : stack.pop());
} else if (command.equals("size")) {
System.out.println(stack.size());
} else if (command.equals("empty")) {
System.out.println(stack.isEmpty() ? 1 : 0);
} else if (command.equals("top")) {
System.out.println(stack.isEmpty() ? -1 : stack.peek());
}
}
}
}
코드 설명
- 키보드로 입력을 받을 수 있도록 Scanner 객체 생성
- Scanner sc = new Scanner(System.in);
- Stack 타입 변수 생성
- Stack<Integer> stack = new Stack<>();
- Integer 타입만 넣을 수 있도록 설정
- n 입력받기
- int n = sc.nextInt();
- 주의! sc.nextLine() 해줘야 함 → 숫자를 입력받으면 줄 바꿈(’\n’)이 남아 있기 때문에 없애줘야 함 (안 한번 버그)
- n 번 반복하면서 명령어 하나하나 처리
- 명령어 하나씩 처리 (if-else 문 사용)
- push X
- push로 시작하는 명령어 찾기
- 공백 기준으로 자름
- 숫자 X를 숫자로 바꾼 후,
- stack.push()를 이용해서 스택에 넣음
- pop
- 명령어가 pop인 경우
- 스택이 비어 있으면 -1 출력
- 아니면 stack.pop()으로 맨 위 값을 빼내고 출력
- size
- 스택에 몇 개가 들어 있는지 stack.size()로 출력
- empty
- 스택이 비어 있으면 1 출력
- 아니면 0 출력
- top
- 스택이 비어 있으면 -1 출력
- 아니면 stack.peek()으로 맨 위 값을 보기만 하고 출력 (빼내지 않음)
- push X
✅ Stack 클래스의 내부적 구조
- 사실 자바의 Stack은 Vector를 상속받아서 만들어집니다.
- Vector는 크기를 자동으로 늘려주는 동적 배열!
- 그래서 스택은 내부에서 배열처럼 데이터를 저장하고 있습니다.
- (원래 스택은 배열로 만들 수도, 연결리스트로 만들 수도 있는데, 자바 기본 Stack은 배열 기반!)
⚠️ Stack 클래스의 단점
Stack 클래스는 오래된 클래스 (Vector 기반이라 좀 느릴 수도 있음)입니다.
그래서 요즘은 ArrayDeque라는 클래스를 더 추천한다고 하네요.
- 더 빠르고
- 더 가볍고
- 최신 버전 최적화가 잘 되어 있음
🔥 ArrayDeque란?
- 이름 그대로
- Array → 내부적으로 배열을 사용
- Deque → “Double Ended Queue” (앞에서도, 뒤에서도 넣고 뺄 수 있음)
- Stack처럼도 쓸 수 있고, Queue처럼도 쓸 수 있음 (엄청 유연함)
- 속도도 아주 빠르고 메모리 효율도 좋음
즉, “최신 자바에서는 Stack 대신 ArrayDeque를 쓰자”가 정석!
ArrayDeque를 사용한 구현은 다음과 같이 하면 됩니다.
import java.io.*;
import java.util.ArrayDeque;
import java.util.Deque;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
Deque<Integer> stack = new ArrayDeque<>();
int n = Integer.parseInt(reader.readLine());
for (int i = 0; i < n; i++) {
String command = reader.readLine();
if (command.startsWith("push")) {
int num = Integer.parseInt(command.split(" ")[1]);
stack.push(num);
} else if (command.equals("pop")) {
writer.write(stack.isEmpty() ? "-1" : String.valueOf(stack.pop()));
writer.newLine();
} else if (command.equals("size")) {
writer.write(String.valueOf(stack.size()));
writer.newLine();
} else if (command.equals("empty")) {
writer.write(stack.isEmpty() ? "1" : "0");
writer.newLine();
} else if (command.equals("top")) {
writer.write(stack.isEmpty() ? "-1" : String.valueOf(stack.peek()));
writer.newLine();
}
}
writer.flush(); // 반드시 출력
}
}
위 코드에서는 Scanner와 System.out.println 대신 BufferedReader와 BufferedWriter를 사용했습니다.
이에 대해 알고 싶다면 아래 포스트 내용을 한 번 읽어 보세요!
[백준] 15552번: 빠른 A+B (자바 Java)
Java에서 빠른 입출력 정리자바(Java)에서 입력(Scanner)과 출력(System.out.println)은 편리하지만, 속도가 느리기 때문에 많은 데이터 처리에서는 시간 초과가 발생할 수 있다.→ 그래서 BufferedReader와 Buff
jyun2e.tistory.com
✅ Stack vs. ArrayDeque 비교
| 항목 | Stack | ArrayDeque |
| 기반 구조 | Vector (옛날 방식, 동기화됨) | 배열 기반 (최신, 빠름) |
| 쓰는 메서드 | push, pop, peek | push, pop, peek (같음!) |
| 속도 | 조금 느림 (동기화 때문에) | 훨씬 빠름 (비동기화) |
| 추천 여부 | ❌ (옛날) | ✅ (요즘 다 씀) |
✅ 왜 ArrayDeque가 좋은 걸까?
- 스레드 동기화를 하지 않아서 Stack보다 빠름
- (Stack은 멀티스레드용이라 항상 lock을 잡는데, 보통은 필요 없음)
- 배열처럼 메모리를 효율적으로 씀
- 앞뒤로 넣고 뺄 수 있어서 Queue처럼도 쓸 수 있음
- Stack 메서드(push, pop, peek, size, isEmpty)를 똑같이 제공
- 하지만 Stack과 달리 Deque는 앞뒤로 넣고 뺄 수 있기 때문에, 위 메서드 말고도 다른 메서드가 추가로 존재합니다.
- 지금은 스택 구조에 대해서 다루고 있기 때문에 다른 메서드에 관해서는 넘어가도록 하겠습니다!
- 중요한 것은 Deque를 사용해도 Stack과 동일한 메서드를 사용할 수 있다는 것!
💡 실전 꿀팁
Deque 타입으로 선언하고 ArrayDeque로 생성해라!
왜냐하면, 인터페이스 타입(Deque)으로 코딩하면 더 유연하고 좋습니다.
Deque<Integer> stack = new ArrayDeque<>();
(인터페이스를 사용하면 나중에 다른 구현체로 바꾸기도 쉬움)
Deque는 인터페이스이기 때문에 사실 ArrayDeque 말고도 LinkedList를 통해 구현할 수도 있습니다.
하지만 Stack/Queue 용도로는 ArrayDeque가 대부분의 상황에서 더 좋아요
LinkedList는 중간 삽입/삭제가 필요한 경우에만 고려하는 게 좋습니다.
📌 정리 한 줄
“ArrayDeque는 빠르고 좋은 현대식 스택이다! push, pop, peek 메서드를 쓰면 Stack처럼 완벽하게 동작한다!”
'알고리즘 > 백준' 카테고리의 다른 글
| [백준] 15552번: 빠른 A+B (자바 Java) (0) | 2025.04.29 |
|---|---|
| [백준] 실버 Ⅳ : 30458번 팰린드롬 애너그램 (0) | 2025.03.16 |
| [백준] 골드V : 1074번 Z (0) | 2025.02.05 |
| [백준] 실버 II : 2630번 색종이 만들기 (0) | 2025.02.05 |
| [백준] 실버Ⅳ: 1065번 (0) | 2024.08.30 |