호우동의 개발일지

Today :

article thumbnail

스택

스택

특징

  • Last in First Out(LIFO)
    → 가장 최근에 들어온 데이터가 가장 먼저 나감
  • 입력의 역순 출력이 필요한 이유
    • 에디터에서 되돌리기 기능
    • 함수호출에서 복귀주소 기능

 

 

 

 

ADT

  • 객체 : 0개 이상의 원소를 가지는 유한 리스트
  • 연산 :
    • create(size) ::= 최대 크기가 size인 빈 스택을 생성
    • is_empty(s) ::= 스택 s가 비어있는지를 검사
    • is_full(s) ::= 스택 s가 가득 찼는가를 검사
    • push(s, x) ::= 스택 s의 맨 위에 요소 x를 추가
    • pop(s) ::= 스택 s의 맨 위에 있는 요소를 삭제
    • peek(s) ::= 스택 s의 맨 위에 있는 요소를 삭제하지 않고 반환

 

 

 

구현

배열을 이용한 스택

  • 1차원 배열 stack [MAX_STACK_SIZE] 사용
  • 스택이 공백상태이면 top은 -1
  • 가장 먼저 들어온 요소는 stack [0]에 저장
  • 가장 최근에 들어온 요소는 stack [top]에 저장
  • top이 MAX_STACK_SIZE-1이면 스택은 포화상태
#include <stdio.h>
#include <stdlib.h>

#define MAX_STACK 100

int stack[MAX_STACK];
int top = -1;
void error(const char* msg){
    printf("%s\n",msg);
    exit(1);
}
int isFull()
{
    return (top == MAX_STACK-1);
}
int isEmpty(){
    return (top == -1);
}
//삽입함수
void push(int item){
    if(isFull()) error("큐가 다 찼습니다");
    stack[++top] = item;
}
//삭제함수
int pop(){
    if(isEmpty()) error("큐가 비었습니다");
    return stack[top--];
}
//피크함수
int peek(){
    if(isEmpty()) error("큐가 비었습니다");
    return stack[top];    
}

int main(){
    push(3);
    push(2);
    push(1);

    printf("%d\n",pop());
    printf("%d\n",pop());
    printf("%d\n",pop());
    printf("%d\n",pop());
}

출력
출력

 

 

 

스택 구조체 배열

  • 복잡한 자료형이 섞여있을 경우 → 스택 구조체로 구현
// 구조체 element 정의
typedef struct{
    int number;
    char* name;
}element;

... 

element stack[MAX_STACK]; // stack을 element 자료형을 선언

...

//스택에 넣을때 매개변수를 구조체 형식으로
void push(element item){
    if(isFull()) error("큐가 다 찼습니다");
    stack[++top] = item;
}
//반환을 구조체로 해야하기 때문에 element로 반환
element pop(){
    if(isEmpty()) error("큐가 비었습니다");
    return stack[top--];
}
//마찬가지
element peek(){
    if(isEmpty()) error("큐가 비었습니다");
    return stack[top];    
}

int main(){
    element in = {1,"hong"};
    push(in);

    element out = pop();
    printf("%s\n",out.name);
    printf("%d",out.number);
}

 

 

 

 

스택을 함수 매개변수로 전달

#include <stdio.h>
#include <stdlib.h>

#define MAX_STACK 100

typedef struct{
    int top;
    int data[MAX_STACK];
}Stack;

void init(Stack* s){
    s->top = -1;
}
void error(const char* msg){
    printf("%s\n",msg);
    exit(1);
}
int isFull(Stack* s)
{
    return (s->top == MAX_STACK-1);
}
int isEmpty(Stack* s){
    return (s->top == -1);
}
void push(Stack* s,int item){
    if(isFull(s)) error("큐가 다 찼습니다");
    s ->data[++(s->top)] = item;
}
int pop(Stack* s){
    if(isEmpty(s)) error("큐가 비었습니다");
    return s->data[(s->top)--];
}
int peek(Stack* s){
    if(isEmpty(s)) error("큐가 비었습니다");
    return s->data[(s->top)];    
}

int main(){
    Stack s;
    init(&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));

}

 

 

 

 

 

동적 배열 스택

#include <stdio.h>
#include <stdlib.h>

#define MAX_STACK 100

typedef struct{
    int top;
    int* data;// 동적이니까 포인터
    int capacity;
}Stack;

void init(Stack* s){
    s->top = -1;
    s->capacity = 1;
    s->data = (int *)malloc(sizeof(int));
}
void error(const char* msg){
    printf("%s\n",msg);
    exit(1);
}
int isFull(Stack* s)
{
    return (s->top == MAX_STACK-1);
}
int isEmpty(Stack* s){
    return (s->top == -1);
}
void push(Stack* s,int item){
    if(isFull(s)){
        s -> capacity *= 2;
        s ->data = (int *)realloc(s -> data ,s -> capacity * sizeof(int));
    }
    s ->data[++(s->top)] = item;
}
int pop(Stack* s){
    if(isEmpty(s)) error("큐가 비었습니다");
    return s->data[(s->top)--];
}
int peek(Stack* s){
    if(isEmpty(s)) error("큐가 비었습니다");
    return s->data[(s->top)];    
}

int main(){
    Stack s;
    init(&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));
    free(s.data); // data 할당 해제

}

 

 

 

 

스택 응용 : 괄호검사

  • 조건
    • 왼쪽 괄호의 개수와 오른쪽 괄호의 개수가 같아야 한다.
    • 같은 괄호에서 왼쪽 괄호는 오른쪽 괄호보다 먼저 나와야 한다.
    • 괄호 사이에는 포함 관계만 존재한다

  • 괄호의 종류 : 대괄호[], 중괄호{}, 소괄호()


  • 잘못된 사용 예
    • (a(b)
    • a(b))
    • a {b(c [d] e] f}

 

 

 

 

 

괄호 검사 알고리즘

  1. 괄호를 처음부터 하나씩 탐색한다.


  2. 해당 인덱스가 왼쪽 괄호 { [ ( 이면 스택에 삽입, 오른쪽 괄호면 } ] ) 스택 가장 맨 위에 있는 것을 꺼내서 비교
    1. 같은 쌍이면 꺼낸 왼쪽 괄호 삭제 후 다음 인덱스를 2번 과정 반복
    2. 다른 쌍이거나 인덱스가 비어있으면 오류 보고 → 잘못됐음을 보고


  • 해당 알고리즘 가능한 이유:
    • LIFO 특징 때문에 가능
      → {(})같은 모양은 존재하지 않는다. 즉 ‘(’이 들어갔을 때 옳은 식이 되려면 바로 ‘)’이 나와야 한다.
      → 스택에 LIFO 특성을 이용하면 쉽게 구현 가능하다.


  • 구현
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STACK 100

typedef struct{
    int top;
    char data[MAX_STACK];
}Stack;

void init(Stack* s){
    s->top = -1;
}
void error(const char* msg){
    printf("%s\n",msg);
    exit(1);
}
int isFull(Stack* s)
{
    return (s->top == MAX_STACK-1);
}
int isEmpty(Stack* s){
    return (s->top == -1);
}
void push(Stack* s,char item){
    if(isFull(s)) error("큐가 다 찼습니다");
    s ->data[++(s->top)] = item;
}
char pop(Stack* s){
    if(isEmpty(s)) error("큐가 비었습니다");
    return s->data[(s->top)--];
}
char peek(Stack* s){
    if(isEmpty(s)) error("큐가 비었습니다");
    return s->data[(s->top)];    
}
int check(Stack* s,const char* str,int len){
    for(int i = 0; i < len; i++){
        switch(str[i]){
            case'{':case'[':case'(': // 왼쪽 괄호이면 스택에 삽입
            push(s,str[i]);
            break;
            case'}':case']':case')': // 오른쪽 괄호이면 스택에서 빼서 현재 str이랑 비교
            if(isEmpty(s)) return 0;
            char top = pop(s);
            if(!((str[i] == '}' && top == '{') || (str[i] == ']' && top == '[') || (str[i] == ')' && top == '('))) {
                return 0;
            }
            break;
        }
    }
    if(isEmpty(s)) return 1;
    else return 0;
}

int main(){
    Stack s;
    init(&s);
    const char* str = "{()}";
    int len = strlen(str);
    printf("%d",check(&s,str,len));
}

 

 

 

 

수식 표기 및 계산 순서

계산 순서 표
계산 순서 표




컴퓨터에서의 수식 계산 순서

  • 중위 표기식 → 후위 표기식 → 계산
    • 2+34x → 234+x → 14
    • 스택 사용

 

 

 

후위 표기식 계산

  • 동작 원리
    1. 수식을 왼쪽에서 오른쪽으로 탐색
    2. 피연산자이면 스택에 저장, 연산자이면 필요한 수만큼 스택에 꺼내 연산
    3. 연산의 결과를 다시 스택에 저장

후위 표기식 계산 과정
후위 표기식 계산 과정

 

 

  • Quiz
    • 여기서 가장 먼저 수행되는 연산은 -> B+E
A B E + D * -

 

 

 

 

중위표기식

  • 중위 표기법과 후위 표기법의 공통점 = 피연산자의 순서 동일
    → 연산자의 순서만 다름(우선순위 순서) → 연산자만 스택에 저장해 놨다가 출력하면 됨
    • 2+34* → 234*+ 이런 식으로 변환 가능
  • 알고리즘
    1. 중위 표기 수식을 순서대로 탐색
    2. 피연산자를 만나면 후위 표기식으로 출력
    3. 왼쪽 괄호는 우선순위가 가장 낮은 연산자로 취급
    4. 오른쪽 괄호가 나오면 스택에서 왼쪽 괄호 위에 쌓여있는 모든 연산자 출력
    5. 현재 연산자보다 우선순위가 높거나 같은 연산자는 모두 출력한 후 현재 연산자를 스택에 넣음
    6. 입력 수식이 끝나면 스택에 남은 연산자들을 모두 pop()해서 출력

 

 

 

 

미로탐색문제

  • 현재 위치에서 가능한 방향을 스택에 저장해 놓았다가
    막다른 길을 만나면 스택에서 다음 탐색 위치를 꺼낸다

미로탐색 문제 스택
미로탐색 문제 스택 그림