1. 스택
-
스택(Stack)
- 물건을 쌓아 올린 듯한 자료구조 (더미)
-
특징: 후입선출(LIFO: Last-In First-Out)
- 가장 나중에(최근에) 들어온 데이터가 가장 먼저 나가는 구조
top에서만push,pop,peek가 발생하여
-
구조
- Top(상단): 데이터의 삽입과 삭제가 일어나는 위치
- Bottom(하단): 스택의 가장 밑바닥
1.1. Time Complexity
- Search: (LIFO 특성상 비효율적)
- Insert/Delete:
1.2. Applications
- 재귀, 백트래킹, 임시 저장 공간 등에서 사용
- 함수 호출 스택
- 백트래킹 알고리즘 (DFS, 퍼즐 문제 등)
- 언어 처리기, 컴파일러 (괄호 검사, 수식 계산 등)
- 웹 브라우저 뒤로 가기, undo 기능 등 이전 상태 복원
1.3. 교재에서 소개된 스택
- 배열 구조의 스택
- 스택 상단에 대한 인덱스를 별도로 저장
2. 스택의 추상 자료형 및 연산
-
주요 연산
init(): 스택 초기화push(e): 요소e를 스택 맨 위(Top)에 추가 (삽입)pop(): 스택 맨 위에 있는 요소를 꺼내서 반환 (삭제)peek(): 스택 맨 위에 있는 요소를 삭제하지 않고 반환 (조회)is_empty(): 스택이 비어있는지 검사 (True/False)is_full(): 스택이 가득 찼는지 검사 (True/False)
-
오류 상황
- 오버플로(Overflow): 스택이 가득 찬 상태에서 데이터 입력을 시도할 때 발생
- 언더플로(Underflow): 스택이 비어있는 상태에서 데이터 삭제(pop)나 조회를 시도할 때 발생
3. 배열을 이용한 스택 구현
-
구조: 1차원 배열(
data[MAX_SIZE])과top변수를 사용 -
Top의 초기값:
-1(스택이 비어있음을 의미) -
상태 검사
- 공백 상태:
top == -1 - 포화 상태:
top == (MAX_SIZE - 1)
- 공백 상태:
-
연산 동작 원리
- Push:
top을 1 증가시킨 후, 해당 인덱스에 데이터 저장 (data[++top] = e) - Pop: 현재
top위치의 데이터를 반환하고top을 1 감소 (return data[top--])
- Push:
4. 스택의 활용 사례
스택은 자료의 순서를 뒤집거나, 되돌리기 기능 등에 주로 사용된다.
4.1. 기본 활용
-
문자열 뒤집어 출력하기
- 문자열을 순서대로 스택에 Push 한 뒤, Pop 하여 출력하면 역순이 된다.
-
구조체 저장
- 단순 데이터뿐만 아니라 구조체(예: 함수 호출 정보 등)도 스택의 요소로 저장할 수 있다.
4.2. 괄호 검사
소스 코드나 수식에서 괄호의 짝이 맞는지 검사하는 알고리즘
-
검사 조건
- 왼쪽/오른쪽 괄호의 개수가 같아야 함
- 왼쪽 괄호가 오른쪽보다 먼저 나와야 함
- 서로 다른 괄호가 교차하여 쌍을 이루면 안 됨
-
알고리즘
- 왼쪽 괄호(
(,{,[)를 만나면 Push - 오른쪽 괄호(
),},])를 만나면 Pop 하여 짝이 맞는지 검사 - 문자열 끝까지 처리 후 스택이 비어있으면 성공, 남아있거나 중간에 짝이 안 맞으면 실패
- 왼쪽 괄호(
4.3. 수식 계산기
컴퓨터가 수식을 계산하기 쉽게 하기 위해 후위 표기식(Postfix)을 사용한다.
- 수식 표기법
- 중위(Infix):
A + B(사람이 사용) - 후위(Postfix):
A B +(컴퓨터 및 스택 사용에 적합)
- 중위(Infix):
| 수식의 표현 방법 | 전위(prefix) | 중위(infix) | 후위(postfix) |
|---|---|---|---|
| 예 1 | + A B | A + B | A B + |
| 예 2 | + 5 * A B | 5 + A * B | 5 A B * + |
-
후위 표기식의 계산
- 피연산자(숫자)는 Push
- 연산자를 만나면 피연산자 2개를 Pop 하여 계산 후, 결과를 다시 Push
-
중위 표기식 → 후위 표기식 변환
- 피연산자는 바로 출력
- 연산자는 스택에 저장하되, 우선순위에 따라 처리 (스택 안의 연산자가 우선순위가 높거나 같으면 꺼내서 출력 후 현재 연산자 Push)
- 오른쪽 괄호가 나오면 왼쪽 괄호가 나올 때까지 스택의 연산자를 모두 Pop 하여 출력
5. 시스템 스택과 순환 호출
5.1. 시스템 스택
-
시스템 스택(System Stack)
- 함수 호출 시 복귀 주소, 매개 변수, 지역 변수 등을 저장하는 메모리 공간
- 함수가 실행되면 활성화 레코드(Activation Record)가 스택에 쌓이고, 함수가 종료되면 스택에서 제거되어 이전 함수로 돌아간다.
-
활성화 레코드에 저장되는 정보
- program counter: 호출된 함수가 종료되면 되돌아갈 프로그램의 위치
- 매개변수
- 지역변수
-
시스템 스택을 사용하는 프로그래밍 기법
- 순환(recursion): 어떤 함수가 자기 자신을 다시 호출하여 문제를 해결한다.
5.2. 순환
-
순환(Recursion)
- 어떤 함수가 자기 자신을 다시 호출하여 문제를 해결하는 기법
-
구조
- 문제를 더 작은 단위로 쪼개어 호출
- 반드시 종료 조건(Base Case)이 있어야 함 (없으면 무한 루프)
-
예제
- 팩토리얼(Factorial):
n! = n * (n-1)!(종료 조건: n=1일 때 1 반환) - 하노이의 탑(Hanoi Tower): 원판을 옮기는 문제를 재귀적으로 해결
n-1개 원판을 임시 기둥으로 이동- 가장 큰 원판을 목표 기둥으로 이동
- 임시 기둥의
n-1개 원판을 목표 기둥으로 이동
- 팩토리얼(Factorial):
void hanoi_tower(int n, char from, char tmp, char to) {
if (n == 1) printf("원판 1: %c --> %c\n", from, to);
else {
hanoi_tower(n - 1, from, to, tmp);
printf("원판 %d: %c --> %c\n", n, from, to);
hanoi_tower(n - 1, tmp, from, to);
}
}