Foundry
자료구조
입문
핵심

스택 (Stack)

LIFO(Last In First Out) 원칙의 선형 자료구조

스택 활용 (괄호 검사, DFS)

스택의 LIFO 특성을 활용한 대표적인 실무 알고리즘들

1. 괄호 유효성 검사

입력: "({[]})"

스택 변화:
( → [( ]
{ → [( { ]
[ → [( { [ ]
] → [( { ]     ← [ 와 매칭!
} → [( ]       ← { 와 매칭!
) → [ ]        ← ( 와 매칭!
→ 스택 비어있음 → 유효

입력: "({)}"
( → [( ]
{ → [( { ]
) → { 와 ) 가 맞지 않는다. 무효
def is_valid(s):
    stack = []
    pairs = {')':'(', '}':'{', ']':'['}
    for ch in s:
        if ch in '({[':
            stack.append(ch)
        elif ch in ')}]':
            if not stack or stack[-1] != pairs[ch]:
                return False
            stack.pop()
    return len(stack) == 0

2. DFS (깊이 우선 탐색)

    1
   / \
  2   3
 / \
4   5

스택 DFS: [1] → [3,2] → [3,5,4]
→ 방문: 1 → 2 → 4 → 5 → 3

3. 콜 스택 (함수 호출)

main() 호출
  ↓ 스택: [main]
  foo() 호출
    ↓ 스택: [main, foo]
    bar() 호출
      ↓ 스택: [main, foo, bar]
    bar() 반환
      ↓ 스택: [main, foo]
  foo() 반환
    ↓ 스택: [main]
main() 반환
  ↓ 스택: []

스택 오버플로우: 재귀 너무 깊으면 스택 메모리 초과!

4. 후위 표기식 계산

중위: 3 + 4 * 2
후위: 3 4 2 * +

계산:
3 → [3]
4 → [3, 4]
2 → [3, 4, 2]
<em> → [3, 8]    (4</em>2)
+ → [11]      (3+8)

스택 활용 정리

활용핵심 원리
괄호 검사열림→push, 닫힘→pop 매칭
DFS다음 방문할 노드 push
콜 스택함수 호출→push, 반환→pop
후위 표기식피연산자 push, 연산자→pop 2개
Undo/Redo행동 push, 되돌리기 pop
뒤로가기페이지 push/pop
면접에서 이렇게 나옵니다

Q.스택이 활용되는 실무 사례를 3가지 이상 말해주세요.

사례왜 스택인가
함수 호출 관리가장 나중에 부른 함수가 가장 먼저 끝난다
되돌리기와 다시하기방금 한 작업을 먼저 취소해야 한다
브라우저 뒤로가기마지막에 본 페이지로 돌아간다
괄호와 태그 짝 검사가장 최근에 열린 것이 먼저 닫혀야 한다
수식 계산과 파싱연산자 우선순위를 나중 것부터 처리한다
깊이 우선 탐색방금 발견한 노드부터 파고든다

공통점은 가장 최근 것이 먼저 처리돼야 한다는 점입니다. 이 문장이 성립하면 스택입니다.

흔한 실수: 재귀와 스택을 다른 것으로 설명하는 것. 재귀는 언어가 대신 관리해 주는 스택입니다. 그래서 재귀로 쓴 코드는 명시적 스택으로 바꿀 수 있습니다.

Q.스택으로 괄호 유효성 검사를 어떻게 구현하나요?

여는 괄호는 쌓고, 닫는 괄호를 만나면 꺼내서 짝이 맞는지 봅니다.

만난 글자처리
여는 괄호스택에 넣는다
닫는 괄호스택이 비었으면 무효. 꺼낸 것과 짝이 안 맞으면 무효
끝까지 봤다스택이 비어 있어야 유효

({[]}) 는 통과하고 (] 는 두 번째 조건에서, (( 는 마지막 조건에서 걸립니다.

시간은 O(n), 공간은 최악에 O(n)입니다.

흔한 실수: 개수만 세는 것. 여는 괄호와 닫는 괄호 수가 같아도 )( 처럼 순서가 틀릴 수 있습니다. 종류가 하나뿐이라면 카운터로 충분하지만, 종류가 섞이면 무엇이 열렸는지 기억해야 하므로 스택이 필요합니다.

Q.함수 호출 스택(Call Stack)이 뭔가요?

함수를 부를 때마다 그 함수의 지역변수, 매개변수, 돌아갈 주소를 한 덩어리로 쌓아두는 메모리 영역입니다. 함수가 끝나면 그 덩어리를 걷어냅니다.

main 이 a 를 부르고 a 가 b 를 부른 상태입니다. 위가 스택의 top 입니다.

프레임담는 것
b지역변수와 돌아갈 주소(a 안의 위치)
a지역변수와 돌아갈 주소(main 안의 위치)
main지역변수

스택인 이유는 가장 나중에 부른 함수가 가장 먼저 끝나기 때문입니다. 오류가 났을 때 보는 스택 트레이스가 이 쌓인 순서를 그대로 출력한 것입니다.

흔한 실수: 스택 오버플로를 "메모리가 부족하다"로 설명하는 것. 전체 메모리가 아니라 스택에 할당된 영역(보통 수백 KB에서 몇 MB)을 넘긴 것입니다. 원인은 대개 종료 조건이 없는 재귀입니다.

Q.스택과 큐의 차이를 실무 예시로 설명해주세요

꺼내는 순서가 반대입니다. 스택은 마지막에 넣은 것을, 큐는 처음 넣은 것을 먼저 꺼냅니다.

구분스택
순서나중에 넣은 것부터먼저 넣은 것부터
실무 예함수 호출 기록, 되돌리기, 괄호 검사주문 처리, 알림 발송, 작업 대기열
탐색깊이 우선너비 우선

무엇을 고르는지는 가장 최근 것이 중요한가, 가장 오래 기다린 것이 중요한가로 갈립니다. 되돌리기는 방금 한 일을 취소해야 하므로 스택이고, 주문 처리는 먼저 온 사람을 먼저 처리해야 하므로 큐입니다.

흔한 실수: 우선순위 큐를 큐의 한 종류로 설명하는 것. 이름은 큐지만 꺼내는 기준이 도착 순서가 아니라 우선순위값이라 성질이 다릅니다.

Q.브라우저 뒤로가기는 어떤 자료구조로 구현하나요?

스택 두 개를 씁니다. 뒤로 갈 목록과 앞으로 갈 목록입니다.

동작뒤로 스택앞으로 스택
새 페이지 방문현재 페이지를 넣는다비운다
뒤로꺼내서 현재로이전 현재를 넣는다
앞으로이전 현재를 넣는다꺼내서 현재로

핵심은 새 페이지를 방문하면 앞으로 스택을 비운다는 점입니다. 그 갈래는 더 이상 이어지지 않기 때문입니다. A, B, C 를 보고 두 번 뒤로 가서 A 에 있다가 D 로 가면 B 와 C 는 다시 앞으로 갈 수 없습니다.

흔한 실수: 큐나 리스트 하나로 충분하다고 답하는 것. 커서를 가진 리스트로도 만들 수 있지만, 그때도 커서 뒤쪽을 잘라내는 처리를 직접 해야 합니다. 스택 둘은 그 처리가 자연히 됩니다.

먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.

읽었으면 문제로 확인해보세요

자료구조 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.