자료구조 4. 스택
코드
- 모든 코드는 깃허브에서 확인하실 수 있습니다.
스택(stack)
- 데이터를 쌓는 형태의 자료구조
- 데이터를 추가할 때는 맨 위에만 추가 가능(
push
) - 데이터를 삭제할 때는 맨 위에만 삭제 가능(
pop
) - 이러한 구조를 LIFO(Last In First Out)라고 부름
- 데이터를 추가할 때는 맨 위에만 추가 가능(
위 그림처럼
top
을 이용해 데이터의 상단을 표시push
를 한다면top
이1
증가pop
을 한다면top
이1
감소
stack의 기능
isFull
: 스택이 포화상태인지 확인isEmpty
: 스택이 비어있는지 확인push
: 스택의 맨 위에 새로운 값을 추가isFull
로 포화상태가 아닌것을 확인 후 값 추가
pop
: 스택의 맨 위의 값을 제거하고 반환isEmpty
로 스택이 비어있지 않았다는 것을 확인 후 값 삭제
peek
: 스택의 맨 위의 값을 확인isEmpty
로 스택이 비어있지 않았다는 것을 확인 후 값 확인
c언어
스택 구조체 정의
1 2 3 4 5
typedef struct { int elements[MAX_STACK_SIZE]; int top; } Stack;
IsFull
1 2 3 4
int IsFull(const Stack* stack) { return (stack->top == sizeof(stack->elements) - 1); }
IsEmpty
1 2 3 4
int IsEmpty(const Stack* stack) { return (stack->top == -1); }
Push
1 2 3 4 5 6 7
void Push(Stack* stack, int value) { assert(!IsFull(stack)); ++(stack->top); stack->elements[stack->top] = value; }
assert
를 이용해 스택이 포화상태라면 에러 발생
Pop
1 2 3 4 5 6
int Pop(Stack* stack) { assert(!IsEmpty(stack)); return stack->elements[stack->top--]; }
- 데이터를 삭제하지 않고,
top
을 감소시키는 것만으로 삭제한 것과 비슷한 효과를 냄
- 데이터를 삭제하지 않고,
Peek
1 2 3 4 5 6
int Peek(const Stack* stack) { assert(!IsEmpty(stack)); return stack->elements[stack->top]; }
이 방법 외에
realloc
을 이용해 동적으로 스택의 크기를 늘리는 방법도 존재
괄호 검사 문제(valid parentheses problem)
괄호가 쌍이 되는지 검사하는 문제
- 예)
{arr[Func(a)-(i + j)]}
- 예)
조건
- 왼쪽 괄호와 오른쪽 괄호의 개수가 같아야 함
- 같은 종류의 괄호에서 왼쪽 괄호가 오른쪽 괄호보다 먼저 나와야 함
- 서로 다른 종류의 괄호 쌍이 교차되면 안됨
풀이법
- 문자열의 왼쪽부터 검사
- 왼쪽 괄호면 스택에
push
- 오른쪽 괄호면 스택을
pop
한 후 같은 종류의 괄호인지 검사- 같은 종류의 괄호면
continue
- 다른 종류의 괄호면
false
반환
- 같은 종류의 괄호면
- 그 외 일반 문자면
continue
- 왼쪽 괄호면 스택에
- 검사가 끝나면
true
반환
- 문자열의 왼쪽부터 검사
c언어
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
int CheckParanthesis(char* str) { Stack paranthesisStack = { .top = -1 }; while (*str != '\0') { switch (*str) { case '[': case '{': case '(': Push(¶nthesisStack, *str); break; case ']': if (IsEmpty(¶nthesisStack) || Pop(¶nthesisStack) != '[') { return 0; } break; case '}': if (IsEmpty(¶nthesisStack) || Pop(¶nthesisStack) != '{') { return 0; } break; case ')': if (IsEmpty(¶nthesisStack) || Pop(¶nthesisStack) != '(') { return 0; } break; } ++str; } return 1; }
후위 표기법 계산
컴파일러는 사람이 작성한 수식(중위 표기법)을 후위 표기법으로 변환하여 계산
- 중위 표기법:
2+3*4
- 후위 표기법:
234*+
- 스택을 이용하여 중위 표기법을 후위 표기법으로 변환할 수 있음
- 마찬가지로 스택을 이용하여 후위 표기법을 계산할 수 있음
- 중위 표기법:
중위 표기법 -> 후위 표기법
- 중위 표기법의 왼쪽부터 검사
- 숫자면 결과 문자열에
append
*
,/
면 스택에push
+
,-
면 스택의 맨 위에*
,/
가 있는지 검사- 있다면
pop
하여 결과 문자열에append
*
,/
유무 상관 없이 스택에push
- 있다면
(
면 스택에push
)
면 스택을pop
하여(
가 나올때까지pop
한 결과를 결과 문자열에append
- 숫자면 결과 문자열에
- 스택이 빌 때까지
pop
하여 결과 문자열에append
- 중위 표기법의 왼쪽부터 검사
후위 표기법 계산
- 후위 표기법의 왼쪽부터 검사
- 숫자면 스택에
push
*
,/
,+
,-
면 스택에서 2번pop
하여 계산 후 결과를 스택에push
- (나중에
pop
된 숫자)연산
(먼저pop
된 숫자)
- (나중에
- 숫자면 스택에
- 스택을
pop
하여 반환
- 후위 표기법의 왼쪽부터 검사
c언어
중위 표기법 -> 후위 표기법
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
int ConvertInfixToPostfix(char* expression, char* result) { if (!CheckParanthesis(expression)) { return 0; } Stack operatorStack = { .top = -1 }; char topOperator; while (*expression != '\0') { switch (*expression) { case '*': case '/': case '(': Push(&operatorStack, *expression); break; case '+': case '-': if (!IsEmpty(&operatorStack)) { topOperator = Peek(&operatorStack); if (topOperator == '*' || topOperator == '/') { *result = Pop(&operatorStack); ++result; } } Push(&operatorStack, *expression); break; case ')': while ((topOperator = Pop(&operatorStack)) != '(') { *result = topOperator; ++result; } break; default: *result = *expression; ++result; break; } ++expression; } while (!IsEmpty(&operatorStack)) { *result = Pop(&operatorStack); ++result; } *result = '\0'; return 1; }
후위 표기법 계산
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
int EvaluatePostfix(char* expression) { Stack stack = { .top = -1 }; int firstNum, secondNum, temp; while (*expression != '\0') { switch (*expression) { case '*': secondNum = Pop(&stack); firstNum = Pop(&stack); temp = firstNum * secondNum; Push(&stack, temp); break; case '/': secondNum = Pop(&stack); firstNum = Pop(&stack); temp = firstNum / secondNum; Push(&stack, temp); break; case '+': secondNum = Pop(&stack); firstNum = Pop(&stack); temp = firstNum + secondNum; Push(&stack, temp); break; case '-': secondNum = Pop(&stack); firstNum = Pop(&stack); temp = firstNum - secondNum; Push(&stack, temp); break; default: Push(&stack, *expression - '0'); break; } ++expression; } return Pop(&stack); }
This post is licensed under CC BY 4.0 by the author.