728x90
C언어로 쉽게 풀어쓴 자료구조: 4장 스택
스택(Stack)이란?
데이터를 쌓아 올린 구조, 후입선출(LIFO, Last In First Out)
➡️ 뒤로가기, 쌓여있는 상자
스택의 추상자료형(ADT)
create(size) = 최대 크기가 size인 공백 스택을 생성한다.
is_full(s) =
if(스택의 원소수 == size) return TRUE;
else return FALSE;
is_empty(s) =
if(스택의 원소수 == 0) return TRUE;
else return FALSE;
push(s, item) =
if( is_full(s) ) return ERROR_StackFull
else 스택의 맨 위에 item을 추가한다.
pop(s) =
if( is_empty(s) ) return ERROR_StackEmpty;
else 스택의 맨 위의 원소를 제거해서 반환한다.
peek(s) =
if( is_empty(s) ) return ERROR_StackEmpty;
else 스택의 맨 위의 원소를 제거하지 않고 반환한다.
4.2 스택의 구현
스택의 요소를 구조체로 하기
#include <stdio.h>
#include <stdlib.h>
#define MAX_STACK_SIZE 100
스택에 저장되는 데이터를 구조체 정의
typedef int element;
typedef struct {
element data[MAX_STACK_SIZE];
int top;
} StackType;
스택 초기화 함수
// 스택 초기화 함수
void init_stack(StackType *s)
{
s->top = -1;
}
공백 상태 검출 함수
int is_empty(StackType *s)
{
return (s->top == -1);
}
포화 상태 검출 함수
int is_full(StackType *s)
{
return (s->top == (MAX_STACK_SIZE - 1));
}
삽입 함수
void push(StackType *s, element item)
{
if (is_full(s)) {
fprintf(stderr, "스택 포화 에러\n");
return;
}
else s->data[++(s->top)] = item;
}
※ fprintf 함수, stderr 스트림
더보기
기능 | printf | fprinf |
기본 출력 | 표준 출력(일반적으로 화면) | 표준 출력(stdout), 오류 스트림(stderr) 또는 파일 등 다양한 스트림을 지정할 수 있다 |
구문 | printf("텍스트\n"); | fprintf(stream, "text\n"); 여기서 stream은 stdout, stderr 또는 파일 포인터일 수 있다 |
오류 처리 | 항상 stdout으로 인쇄 | 더 나은 가시성과 일반 출력과의 분리를 위해 stderr에 오류 메시지를 보낼 수 있다 |
즉시 출력 | 버퍼링됨(인쇄하기 전에 기다릴 수 있음) | stderr(오류 스트림)은 버퍼링되지 않으므로 메시지가 즉시 표시된다 |
파일 출력 | 직접 파일 지원 없음 | fprintf(file, "text\n");를 사용하여 파일에 직접 인쇄할 수 있다. |
사용 사례 예시 | 일반 메시지, 표준 출력 | 오류 또는 경고(stderr), 파일에 기록, 별도의 스트림이 필요한 경우 |
라고 하네요..
삭제 함수
element pop(StackType *s)
{
if (is_empty(s)) {
fprintf(stderr, "스택 공백 에러\n");
exit(1);
}
else return s->data[(s->top)--];
}
피크 함수
element peek(StackType *s)
{
if (is_empty(s)) {
fprintf(stderr, "스택 공백 에러\n");
exit(1);
}
else return s->data[s->top];
}
int main(void)
{
StackType s; //스택을 정적으로 생성한다.
init_stack(&s);
push(&s, 1);
push(&s, 2);
push(&s, 3);
printf("%d\n", pop(&s));
printf("%d\n", pop(&s));
printf("%d\n", pop(&s));
}
4.4 스택의 응용: 괄호 검사 문제
조건
- 왼쪽 괄호의 개수와 오른쪽 괄호의 개수가 같아야 한다.
- 같은 종류의 괄호에서 왼쪽 괄호는 오른쪽 괄호보다 먼저 나와야 한다.
- 서로 다른 종류의 왼쪽 괄호와 오른쪽 괄호 쌍은 서로를 교차하면 안된다.
int check_matching(const char *in)
{
StackType s;
char ch, open_ch;
int i, n = strlen(in); // n= 문자열의 길이
init_stack(&s); // 스택의 초기화
for (i = 0; i < n; i++) {
ch = in[i]; // ch = 다음 문자
switch (ch) {
case '(': case '[': case '{':
push(&s, ch);
break;
case ')': case ']': case '}':
if (is_empty(&s)) return 0;
else {
open_ch = pop(&s);
if ((open_ch == '(' && ch != ')') ||
(open_ch == '[' && ch != ']') ||
(open_ch == '{' && ch != '}')) {
return 0;
}
break;
}
}
}
if (!is_empty(&s)) return 0; // 스택에 남아있으면 오류
return 1;
}
후위 표기 수식 계산
int eval(char exp[])
{
int op1, op2, value, i = 0;
int len = strlen(exp);
char ch;
StackType s;
init_stack(&s);
for (i = 0; i<len; i++) {
ch = exp[i];
if (ch != '+' && ch != '-' && ch != '*' && ch != '/') {
value = ch - '0'; // 입력이 피연산자이면
push(&s, value);
}
else { //연산자이면 피연산자를 스택에서 제거
op2 = pop(&s);
op1 = pop(&s);
switch (ch) { //연산을 수행하고 스택에 저장
case '+': push(&s, op1 + op2); break;
case '-': push(&s, op1 - op2); break;
case '*': push(&s, op1 * op2); break;
case '/': push(&s, op1 / op2); break;
}
}
}
return pop(&s);
}
중위 표기 수식 ➡️ 후위 표기 수식
void infix_to_postfix(char exp[])
{
int i = 0;
char ch, top_op;
int len = strlen(exp);
StackType s;
init_stack(&s); // 스택 초기화
for (i = 0; i<len; i++) {
ch = exp[i];
switch (ch) {
case '+': case '-': case '*': case '/': // 연산자
// 스택에 있는 연산자의 우선순위가 더 크거나 같으면 출력
while (!is_empty(&s) && (prec(ch) <= prec(peek(&s))))
printf("%c", pop(&s));
push(&s, ch);
break;
case '(': // 왼쪽 괄호
push(&s, ch);
break;
case ')': // 오른쪽 괄호
top_op = pop(&s);
// 왼쪽 괄호를 만날때까지 출력
while (top_op != '(') {
printf("%c", top_op);
top_op = pop(&s);
}
break;
default: // 피연산자
printf("%c", ch);
break;
}
}
while (!is_empty(&s)) // 스택에 저장된 연산자들 출력
printf("%c", pop(&s));
}
※ ' ( ' 의 경우 ' ) '를 만나면 Pop
미로 찾기 문제
- 현재 위치에서 이동이 가능한 칸들의 위치를 위, 아래, 왼쪽, 오른쪽의 순서로 스택에 저장
- 스택에서 맨위의 위치를 꺼내어 현재의 위치로 한다음에, 같은 작업은 반복한다.
- 현재의 위치가 출구와 같거나 모든 위치를 다 검사해 볼 때까지 계속 반복한다.
maze_search():
스택 s과 출구 위치 x, 현재 위치를 초기화
while(현재의 위치가 출구가 아니면) do
현재위치를 방문한 것으로 표기
if(현재위치의 위, 아래, 왼쪽, 오른쪽 위치가 아직 방문되지 않았고 갈수 있으면)
then 그 위치들을 스택에 push
if(is_empty(s))
then 실패
else 스택에서 하나의 위치를 꺼내어 현재 위치로 만든다;
성공;
element here = { 1,0 }, entry = { 1,0 };
char maze[MAZE_SIZE][MAZE_SIZE] = {
{ '1', '1', '1', '1', '1', '1' },
{ 'e', '0', '1', '0', '0', '1' },
{ '1', '0', '0', '0', '1', '1' },
{ '1', '0', '1', '0', '1', '1' },
{ '1', '0', '1', '0', '0', 'x' },
{ '1', '1', '1', '1', '1', '1' },
};
// 위치를 스택에 삽입
void push_loc(StackType *s, int r, int c)
{
if (r < 0 || c < 0) return;
if (maze[r][c] != '1' && maze[r][c] != '.') {
element tmp;
tmp.r = r;
tmp.c = c;
push(s, tmp);
}
}
// 미로를 화면에 출력한다.
void maze_print(char maze[MAZE_SIZE][MAZE_SIZE])
{
printf("\n");
for (int r = 0; r < MAZE_SIZE; r++) {
for (int c = 0; c < MAZE_SIZE; c++) {
printf("%c", maze[r][c]);
}
printf("\n");
}
}
int main(void)
{
int r, c;
StackType s;
init_stack(&s);
here = entry;
while (maze[here.r][here.c] != 'x') {
r = here.r;
c = here.c;
maze[r][c] = '.';
maze_print(maze);
push_loc(&s, r - 1, c);
push_loc(&s, r + 1, c);
push_loc(&s, r, c - 1);
push_loc(&s, r, c + 1);
if (is_empty(&s)) {
printf("실패\n");
return;
}
else
here = pop(&s);
}
printf("성공\n");
return 0;
}
728x90
반응형
'C언어로 쉽게 풀어쓴 자료구조' 카테고리의 다른 글
[C언어로 쉽게 풀어쓴 자료구조] 7장 연결리스트 - Ⅱ (0) | 2024.11.23 |
---|---|
[C언어로 쉽게 풀어쓴 자료구조] 6장 연결리스트 - Ⅰ (1) | 2024.11.17 |
[C언어로 쉽게 풀어쓴 자료구조] 5장 큐 (0) | 2024.11.16 |
[C언어로 쉽게 풀어쓴 자료구조] 3장 배열, 구조체, 포인터 (2) | 2024.11.06 |
[C언어로 쉽게 풀어쓴 자료구조] 1장 자료구조와 알고리즘 (2) | 2024.11.05 |