Deque로 Stack 완벽 구현하기
Java API Reference
import java.util.*;
public class StackProblem {
public static void main(String[] args) {
System.out.println("=== Stack 클래스 문제점 ===\n");
System.out.println("1. 레거시 (Java 1.0)");
System.out.println(" - Vector 상속");
System.out.println(" - 오래된 설계\n");
System.out.println("2. 동기화 오버헤드");
System.out.println(" - 모든 메서드 synchronized");
System.out.println(" - 단일 스레드에서도 느림\n");
System.out.println("3. Vector 상속 문제");
System.out.println(" - 중간 삽입/삭제 가능");
System.out.println(" - Stack 의미 위반\n");
System.out.println("4. 비일관적 API");
System.out.println(" - push/pop (Stack)");
System.out.println(" - add/remove (Vector)\n");
System.out.println("=== 해결책 ===");
System.out.println("Deque 인터페이스 사용!");
}
}public class PerformanceComparison {
public static void main(String[] args) {
int n = 1000000;
// Stack 클래스
Stack<Integer> stack = new Stack<>();
long start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
stack.push(i);
}
long time1 = System.currentTimeMillis() - start;
// Deque
Deque<Integer> deque = new ArrayDeque<>();
start = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
deque.push(i);
}
long time2 = System.currentTimeMillis() - start;
System.out.println("=== " + n + "개 push ===");
System.out.println("Stack: " + time1 + "ms");
System.out.println("Deque: " + time2 + "ms");
System.out.println("\nDeque가 훨씬 빠름!");
}
}public class DequeAsStack {
public static void main(String[] args) {
// ArrayDeque로 Stack
Deque<String> stack = new ArrayDeque<>();
System.out.println("=== Stack 연산 ===\n");
// push (추가)
stack.push("A");
stack.push("B");
stack.push("C");
System.out.println("push 후: " + stack);
// [C, B, A]
// peek (조회)
String top = stack.peek();
System.out.println("peek: " + top); // C
System.out.println("stack: " + stack); // 변화 없음
// pop (제거)
String popped = stack.pop();
System.out.println("pop: " + popped); // C
System.out.println("stack: " + stack);
// [B, A]
// 크기 확인
System.out.println("size: " + stack.size());
System.out.println("isEmpty: " + stack.isEmpty());
}
}public class MethodMapping {
public static void main(String[] args) {
System.out.println("=== Stack 메서드 매핑 ===\n");
System.out.println("Stack 메서드 → Deque 메서드");
System.out.println("--------------------------------");
System.out.println("push(e) → push(e)");
System.out.println(" → offerFirst(e)");
System.out.println(" → addFirst(e)\n");
System.out.println("pop() → pop()");
System.out.println(" → pollFirst()");
System.out.println(" → removeFirst()\n");
System.out.println("peek() → peek()");
System.out.println(" → peekFirst()");
System.out.println(" → getFirst()\n");
System.out.println("isEmpty() → isEmpty()");
System.out.println("size() → size()");
}
}public class StackOperations {
public static void main(String[] args) {
Deque<Integer> stack = new ArrayDeque<>();
// 여러 요소 push
for (int i = 1; i <= 5; i++) {
stack.push(i);
System.out.println("push " + i + ": " + stack);
}
System.out.println("\n=== pop 순서 ===");
while (!stack.isEmpty()) {
System.out.println("pop: " + stack.pop());
}
// 5, 4, 3, 2, 1 (LIFO)
// 빈 스택
System.out.println("\n빈 스택 peek: " + stack.peek()); // null
try {
stack.pop(); // 예외
} catch (NoSuchElementException e) {
System.out.println("빈 스택 pop: 예외!");
}
}
}public class ConditionalOperations {
public static void main(String[] args) {
Deque<Integer> stack = new ArrayDeque<>();
// 안전한 pop
Integer value = stack.isEmpty() ? null : stack.pop();
System.out.println("안전한 pop: " + value);
// 조건부 push
stack.push(1);
stack.push(2);
stack.push(3);
if (stack.peek() != null && stack.peek() > 2) {
stack.pop();
System.out.println("3 제거됨");
}
System.out.println("Stack: " + stack);
}
}public class BracketValidation {
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();
}
public static void main(String[] args) {
String[] tests = {"()", "()[]{}", "(]", "([)]", "{[]}"};
for (String test : tests) {
System.out.println(test + ": " + isValid(test));
}
}
}public class PostfixEvaluation {
public static int evaluate(String expression) {
Deque<Integer> stack = new ArrayDeque<>();
for (String token : expression.split(" ")) {
if (isOperator(token)) {
int b = stack.pop();
int a = stack.pop();
int result = calculate(a, b, token);
stack.push(result);
} else {
stack.push(Integer.parseInt(token));
}
}
return stack.pop();
}
private static boolean isOperator(String s) {
return s.equals("+") || s.equals("-") ||
s.equals("*") || s.equals("/");
}
private static int calculate(int a, int b, String op) {
switch (op) {
case "+": return a + b;
case "-": return a - b;
case "*": return a * b;
case "/": return a / b;
default: throw new IllegalArgumentException();
}
}
public static void main(String[] args) {
String expr = "3 4 + 2 * 7 /";
System.out.println("표기식: " + expr);
System.out.println("결과: " + evaluate(expr));
// (3 + 4) * 2 / 7 = 2
}
}public class InfixToPostfix {
public static String convert(String infix) {
StringBuilder result = new StringBuilder();
Deque<Character> stack = new ArrayDeque<>();
for (char c : infix.toCharArray()) {
if (Character.isLetterOrDigit(c)) {
result.append(c);
} else if (c == '(') {
stack.push(c);
} else if (c == ')') {
while (!stack.isEmpty() && stack.peek() != '(') {
result.append(stack.pop());
}
stack.pop(); // '(' 제거
} else { // 연산자
while (!stack.isEmpty() &&
precedence(c) <= precedence(stack.peek())) {
result.append(stack.pop());
}
stack.push(c);
}
}
while (!stack.isEmpty()) {
result.append(stack.pop());
}
return result.toString();
}
private static int precedence(char op) {
switch (op) {
case '+':
case '-': return 1;
case '*':
case '/': return 2;
default: return 0;
}
}
public static void main(String[] args) {
String infix = "A+B*C";
String postfix = convert(infix);
System.out.println("중위: " + infix);
System.out.println("후위: " + postfix);
// ABC*+
}
}public class CallStack {
private Deque<String> callStack = new ArrayDeque<>();
public void enter(String function) {
callStack.push(function);
printStack("Enter");
}
public void exit() {
if (!callStack.isEmpty()) {
String func = callStack.pop();
printStack("Exit " + func);
}
}
private void printStack(String action) {
System.out.println(action + ": " + callStack);
}
public static void main(String[] args) {
CallStack cs = new CallStack();
cs.enter("main()");
cs.enter("foo()");
cs.enter("bar()");
cs.exit();
cs.enter("baz()");
cs.exit();
cs.exit();
cs.exit();
}
}public class UndoManager {
private Deque<String> undoStack = new ArrayDeque<>();
private Deque<String> redoStack = new ArrayDeque<>();
private String current = "";
public void write(String text) {
undoStack.push(current);
current = text;
redoStack.clear();
System.out.println("Write: " + current);
}
public void undo() {
if (undoStack.isEmpty()) {
System.out.println("Undo 불가");
return;
}
redoStack.push(current);
current = undoStack.pop();
System.out.println("Undo: " + current);
}
public void redo() {
if (redoStack.isEmpty()) {
System.out.println("Redo 불가");
return;
}
undoStack.push(current);
current = redoStack.pop();
System.out.println("Redo: " + current);
}
public static void main(String[] args) {
UndoManager um = new UndoManager();
um.write("Hello");
um.write("Hello World");
um.write("Hello World!");
um.undo();
um.undo();
um.redo();
}
}public class MinStack {
private Deque<Integer> stack;
private Deque<Integer> minStack;
public MinStack() {
this.stack = new ArrayDeque<>();
this.minStack = new ArrayDeque<>();
}
public void push(int val) {
stack.push(val);
if (minStack.isEmpty() || val <= minStack.peek()) {
minStack.push(val);
}
}
public void pop() {
int val = stack.pop();
if (val == minStack.peek()) {
minStack.pop();
}
}
public int top() {
return stack.peek();
}
public int getMin() {
return minStack.peek();
}
public static void main(String[] args) {
MinStack ms = new MinStack();
ms.push(5);
ms.push(2);
ms.push(7);
ms.push(1);
System.out.println("최소값: " + ms.getMin()); // 1
ms.pop();
System.out.println("최소값: " + ms.getMin()); // 2
ms.pop();
System.out.println("최소값: " + ms.getMin()); // 2
}
}// * 가 (, ), 빈 문자열 중 하나가 될 수 있을 때
public class Problem1 {
public static boolean checkValidString(String s) {
// 코드 작성
return false;
}
public static void main(String[] args) {
System.out.println(checkValidString("()")); // true
System.out.println(checkValidString("(*)")); // true
System.out.println(checkValidString("(*))")); // true
System.out.println(checkValidString("(())")); // true
}
}정답:
정답 보기
public class Problem1 {
public static boolean checkValidString(String s) {
Deque<Integer> leftStack = new ArrayDeque<>();
Deque<Integer> starStack = new ArrayDeque<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (c == '(') {
leftStack.push(i);
} else if (c == '*') {
starStack.push(i);
} else { // ')'
if (!leftStack.isEmpty()) {
leftStack.pop();
} else if (!starStack.isEmpty()) {
starStack.pop();
} else {
return false;
}
}
}
// 남은 '('를 '*'로 매칭
while (!leftStack.isEmpty() && !starStack.isEmpty()) {
if (leftStack.pop() > starStack.pop()) {
return false;
}
}
return leftStack.isEmpty();
}
}// 각 날짜별로 며칠 후에 더 따뜻한 날이 오는지
public class Problem2 {
public static int[] dailyTemperatures(int[] temps) {
// 코드 작성
return null;
}
public static void main(String[] args) {
int[] temps = {73, 74, 75, 71, 69, 72, 76, 73};
int[] result = dailyTemperatures(temps);
System.out.println("온도: " + Arrays.toString(temps));
System.out.println("대기: " + Arrays.toString(result));
// [1, 1, 4, 2, 1, 1, 0, 0]
}
}정답:
정답 보기
public class Problem2 {
public static int[] dailyTemperatures(int[] temps) {
int n = temps.length;
int[] result = new int[n];
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && temps[stack.peek()] < temps[i]) {
int idx = stack.pop();
result[idx] = i - idx;
}
stack.push(i);
}
return result;
}
}// 히스토그램에서 가장 큰 직사각형 넓이
public class Problem3 {
public static int largestRectangle(int[] heights) {
// 코드 작성
return 0;
}
public static void main(String[] args) {
int[] heights = {2, 1, 5, 6, 2, 3};
int area = largestRectangle(heights);
System.out.println("높이: " + Arrays.toString(heights));
System.out.println("최대 넓이: " + area);
// 10 (5*2)
}
}정답:
정답 보기
public class Problem3 {
public static int largestRectangle(int[] heights) {
Deque<Integer> stack = new ArrayDeque<>();
int maxArea = 0;
int n = heights.length;
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && heights[stack.peek()] > heights[i]) {
int height = heights[stack.pop()];
int width = stack.isEmpty() ? i : i - stack.peek() - 1;
maxArea = Math.max(maxArea, height * width);
}
stack.push(i);
}
while (!stack.isEmpty()) {
int height = heights[stack.pop()];
int width = stack.isEmpty() ? n : n - stack.peek() - 1;
maxArea = Math.max(maxArea, height * width);
}
return maxArea;
}
}// ❌ 사용하지 말 것
Stack<String> stack = new Stack<>();
// ✅ 권장
Deque<String> stack = new ArrayDeque<>();Deque<String> stack = new ArrayDeque<>();
// 추가
stack.push("A");
// 조회
String top = stack.peek();
// 제거
String popped = stack.pop();
// 크기/비어있는지
int size = stack.size();
boolean empty = stack.isEmpty();push(e) = offerFirst(e) = addFirst(e)
pop() = pollFirst() = removeFirst()
peek() = peekFirst() = getFirst()
✅ 괄호 검증
✅ 후위 표기식
✅ 함수 호출 스택
✅ Undo/Redo
✅ DFS
❌ Queue 필요 (FIFO)
❌ 양방향 접근
1. 빠름 (동기화X)
2. 메모리 효율적
3. 일관된 API
4. 현대적 설계