Java ArrayList로 스택(Stack) 문제 완벽 정복하기

scminy0527·2026년 3월 8일
post-thumbnail

코딩테스트를 준비하다 보면 스택(Stack) 관련 문제를 정말 자주 마주치게 됩니다.
배열(int[])로도 구현할 수 있지만, 크기가 고정되어 있어 불편한 경우가 많죠.
이번 글에서는 ArrayList를 활용해서 스택처럼 사용하는 방법과 대표 예제들을 살펴볼게요.


🔍 스택(Stack)이란?

스택은 LIFO(Last In, First Out) 구조입니다.
즉, 마지막에 넣은 데이터가 가장 먼저 나옵니다.

[Push]   1 → 2 → 3  (넣기)
[Pop]    3 → 2 → 1  (꺼내기)

현실 예시로는 접시 쌓기를 생각하면 됩니다.
마지막에 올린 접시를 제일 먼저 꺼내야 하죠.


📌 배열 vs ArrayList

비교 항목배열 (int[])ArrayList
크기고정 (선언 시 결정)동적 (자동 확장)
유연성낮음높음
코딩테스트 활용도보통높음 ✅

배열은 int[] stack = new int[100]; 처럼 크기를 미리 잡아야 해서
입력 크기가 유동적인 문제에서는 실수하기 쉽습니다.
ArrayList를 쓰면 이런 걱정이 사라집니다!


🛠️ ArrayList로 스택 기본 구현

import java.util.ArrayList;

public class StackWithArrayList {
    public static void main(String[] args) {
        ArrayList<Integer> stack = new ArrayList<>();

        // Push: 데이터 넣기
        stack.add(1);
        stack.add(2);
        stack.add(3);
        System.out.println("스택 상태: " + stack); // [1, 2, 3]

        // Peek: 맨 위 데이터 확인 (꺼내지 않음)
        int top = stack.get(stack.size() - 1);
        System.out.println("Top: " + top); // 3

        // Pop: 맨 위 데이터 꺼내기
        int popped = stack.remove(stack.size() - 1);
        System.out.println("Pop된 값: " + popped); // 3
        System.out.println("스택 상태: " + stack); // [1, 2]

        // isEmpty: 비어있는지 확인
        System.out.println("비어있나요? " + stack.isEmpty()); // false
    }
}

핵심 메서드 정리

스택 연산ArrayList 코드설명
pushstack.add(value)맨 위에 추가
popstack.remove(stack.size() - 1)맨 위 제거 후 반환
peekstack.get(stack.size() - 1)맨 위 확인 (제거 X)
isEmptystack.isEmpty()비어있는지 확인
sizestack.size()현재 원소 개수

🧩 예제 1: 올바른 괄호 검사

문제: 주어진 문자열에서 괄호가 올바르게 열리고 닫혔는지 확인하세요.

  • "(())" → true
  • "(()(" → false
  • ")()" → false
import java.util.ArrayList;

public class BracketCheck {
    public static boolean isValid(String s) {
        ArrayList<Character> stack = new ArrayList<>();

        for (char c : s.toCharArray()) {
            if (c == '(') {
                // 여는 괄호면 push
                stack.add(c);
            } else { // c == ')'
                // 닫는 괄호인데 스택이 비어있으면 false
                if (stack.isEmpty()) return false;
                // 맨 위의 여는 괄호를 pop
                stack.remove(stack.size() - 1);
            }
        }

        // 모두 처리 후 스택이 비어있어야 올바른 괄호
        return stack.isEmpty();
    }

    public static void main(String[] args) {
        System.out.println(isValid("(())"));  // true
        System.out.println(isValid("(()("));  // false
        System.out.println(isValid(")(()"));  // false
    }
}

핵심 아이디어: 여는 괄호 (는 push, 닫는 괄호 )는 pop.
모든 처리 후 스택이 비어있으면 올바른 괄호입니다.


🧩 예제 2: 탑 (스택 응용)

문제: 수열이 주어졌을 때, 각 원소의 오른쪽에서 처음으로 자신보다 큰 원소의 인덱스를 구하세요. 없으면 -1.

  • 입력: [2, 1, 4, 3]
  • 출력: [2, 2, -1, -1] (0-indexed)
import java.util.ArrayList;

public class NextGreater {
    public static int[] nextGreaterElement(int[] nums) {
        int n = nums.length;
        int[] result = new int[n];
        // 스택에는 아직 정답을 못 찾은 인덱스를 저장
        ArrayList<Integer> stack = new ArrayList<>();

        for (int i = 0; i < n; i++) {
            // 현재 값이 스택 top의 값보다 크면, top의 정답이 현재 인덱스
            while (!stack.isEmpty() && nums[stack.get(stack.size() - 1)] < nums[i]) {
                int idx = stack.remove(stack.size() - 1);
                result[idx] = i;
            }
            stack.add(i);
        }

        // 스택에 남아있는 인덱스는 정답이 없음 → -1
        while (!stack.isEmpty()) {
            result[stack.remove(stack.size() - 1)] = -1;
        }

        return result;
    }

    public static void main(String[] args) {
        int[] input = {2, 1, 4, 3};
        int[] output = nextGreaterElement(input);
        for (int v : output) System.out.print(v + " "); // 2 2 -1 -1
    }
}

이 패턴은 단조 스택(Monotonic Stack) 이라 불리며, 히스토그램, 온도 문제 등에 자주 등장합니다.


🧩 예제 3: 후위 표기식 계산 (Postfix)

문제: 후위 표기식 "3 4 + 2 *" 를 계산하세요. (결과: 14)

import java.util.ArrayList;

public class PostfixCalculator {
    public static int calculate(String expression) {
        ArrayList<Integer> stack = new ArrayList<>();
        String[] tokens = expression.split(" ");

        for (String token : tokens) {
            if (token.equals("+") || token.equals("-")
                    || token.equals("*") || token.equals("/")) {
                // 피연산자 두 개 꺼내기
                int b = stack.remove(stack.size() - 1);
                int a = stack.remove(stack.size() - 1);

                switch (token) {
                    case "+" -> stack.add(a + b);
                    case "-" -> stack.add(a - b);
                    case "*" -> stack.add(a * b);
                    case "/" -> stack.add(a / b);
                }
            } else {
                // 숫자면 push
                stack.add(Integer.parseInt(token));
            }
        }

        return stack.get(0);
    }

    public static void main(String[] args) {
        System.out.println(calculate("3 4 + 2 *")); // (3+4)*2 = 14
        System.out.println(calculate("5 1 2 + 4 * + 3 -")); // 14
    }
}

✅ 마무리 정리

ArrayList를 스택으로 활용하면 크기 걱정 없이 유연하게 문제를 풀 수 있습니다.
코딩테스트에서 스택 문제를 만났을 때 아래 패턴만 기억하세요!

ArrayList<Integer> stack = new ArrayList<>();

stack.add(value);                        // push
stack.remove(stack.size() - 1);         // pop
stack.get(stack.size() - 1);            // peek
stack.isEmpty();                         // 비어있는지 확인

스택이 자주 쓰이는 문제 유형:

  • ✅ 괄호 유효성 검사
  • ✅ 단조 스택 (다음 큰 원소, 히스토그램)
  • ✅ 후위 표기식 계산
  • ✅ DFS (재귀 대신 스택으로 구현)
  • ✅ 문자열 역순, 뒤로가기 기능 구현

Java에는 java.util.Stack 클래스도 있지만, 공식 문서에서도 Deque 또는 ArrayDeque 사용을 권장합니다.
다만 코딩테스트에서 직관적인 구현을 원한다면 ArrayList도 훌륭한 선택입니다! 🙂

profile
비전공자에서 시작해 Flutter로 실서비스를 만들었고, 더 깊은 개발을 향해 계속 성장 중인 개발자

0개의 댓글