# Courses/자료구조

[자료구조] 4. 스택(Stack)이란? / 연산, 구현방법

illiilli7 2022. 4. 25. 00:54
             
   

이번 포스팅에서는 스택의 개념과 구조, 연산과 함께 스택을 각각 정적 및 동적으로 구현하는 방법에 대해서 정리해보았습니다.

 

📌 주요 개념

✔️ 스택(Stack)이란?
✔️ 스택 vs 리스트 vs 큐 (비교)
✔️ 스택 연산함수
✔️ 스택 구현방법 (정적/동적)

 

 

I. 스택(Stack)이란?

스택은 컴퓨터에서 아주 많이 사용되는 자료구조입니다. 예를 들어, 스마트폰 "뒤로 가기" 키를 눌렀을 때 현재 수행되는 앱이 종료되고 바로 직전에 수행되던 앱이 다시 나타나곤 하는데요, 이때 사용되는 것이 스택입니다.

 

스택(stack)은 말 그대로 '쌓아놓은 더미'를 뜻합니다. 식당에 쌓여있는 접시 더미, 책상에 쌓인 책, 겹겹이 쌓인 상자 모두 스택의 예에 해당합니다. 이 중에서도 가장 대표적인 예시로 '프링글스(Pringles)'를 들 수 있겠습니다.

 

프링글스 - 스택(Stack)
프링글스 - 스택(Stack)

 

※ 후입선출 (LIFO: Last-In First-Out)

 

스택의 가장 큰 특징은 후입선출('LIFO') 입니다. 가장 최근에 들어온 데이터가 가장 먼저 나간다는 의미인데요, 이는 과자 프링글스를 생각하면 쉽습니다. 젤 나중에 넣은 칩 조각을 가장 먼저 먹게 되는 것과 같은 이치죠. 이는 먼저 들어온 데이터가 먼저 나가는 '큐(Queue)' 와의 가장 큰 차이점 이라고도 할 수 있습니다.

스택 vs 큐
스택 vs 큐

 

※ 스택 vs. 큐 vs. 리스트

 

스택, 큐 모두 리스트 자료구조의 특별한 경우이며, 이들 간에는 공통점과 차이점을 지닙니다.

자료구조 공통점 차이점
리스트(List) 선형 자료 구조,
순서가 있다
읽기, 삽입(insert)과 삭제(delete)를 리스트의 어느 곳에서나 행함
스택(Stack) 삽입(insert)과 삭제(delete)를 리스트의 한쪽(top) 에서 행함
(Queue) 삽입(insert)은 리스트의 한쪽(rear)에서 하고, 삭제(delete)는 삽입의 반대쪽(front)에서 행함

 

 

II. 스택의 구조 & 사용

스택은 자료의 출력 순서가 입력 순서의 역순으로 이루어질 때 아주 요긴하게 사용되는 자료구조입니다.

 

문서 편집기에서 '되돌리기(undo)' 기능을 구현하면 바로 직전(최근)에 실행했던 작업이 취소되곤 하지 않나요? 그때 바로 이 스택 자료구조를 사용합니다.

 

스택의 구조
스택의 구조

  • 상단(stack top) : 스택에서 입출력이 이루어지는 부분
  • 하단(stack bottom): 반대쪽 바닥 부분
  • 요소(element): 스택에 저장되는 것
  • 공백 스택(empty stack): 공백 상태의 스택
  • 포화 스택(full stack): 포화 상태의 스택 (풀스택!)

 

 

※ 시스템 스택(Stack) 함수호출

- 시스템 스택(system stack) : 운영체제가 사용하는 스택

 

  • 복귀주소 기억

: 함수의 실행이 끝나면 자신을 호출한 함수로 되돌아갑니다. 이 때 스택은 복귀할 주소를 기억하는 데 사용됩니다. 함수는 호출된 역순으로 되돌아가야 하기 때문입니다.

 

시스템 스택 - 복귀주소 기억
시스템 스택 - 복귀주소 기억

 

  • 활성 레코드

: 시스템 스택에는 함수가 호출될 때마다 활성 레코드(activation record)가 만들어지며, 이곳에 복귀 주소가 저장됩니다. 활성 레코드에 생성되는 정보는 아래와 같습니다.

    - 복귀 주소

    - 프로그램 카운터

    - 매개변수

    - 함수 안에서 선언된 지역변수

 

시스템 스택에는 아래 그림과 같은 순서로 활성 레코드가 만들어졌다가 없어지게 됩니다. 

스택 함수호출

  • 사용 예시

: Undo 기능, 괄호검사, 계산기, 미로탐색 등

 

 

 

III. 스택 추상자료형

스택을 추상 자료형으로 정의해 보았을 때, 스택(Stack)은 후입선출(LIFO)의 접근방법을 유지하는 0개 이상의 요소를 지닌 선형 리스트의 일종이라 할 수 있습니다.

 

1) 스택 ADT (추상데이터타입)

  - 객체: n개의 요소(element)들의 선형 리스트

연산 기능
init() 스택을 초기화
create() 스택을 생성
is_empty(s) 스택이 비어있는지 검사
is_full(s) 스택이 가득 찼는지 검사
push(e) 스택의 맨 위에 요소 e 추가
pop(s) 스택의 맨 위 요소를 삭제
peek(s) 스택의 맨 위 요소를 삭제하지 않고 반환

 

2) 스택의 연산

: 스택에 요소를 추가/삭제하는 연산과, 현재 스택 상태를 검사하는 연산들로 구성됩니다.

연산 기능
top() 스택 맨 위에 있는 데이터 값 반환
push() 스택에 데이터 삽입
pop() 스택에서 데이터 삭제하여 반환
isempty() 스택에 원소가 없으면 'True', 있으면 'False' 값 반환
isfull() 스택에 원소가 없으면 'False', 있으면 'True' 값 반환

 

스택의 연산 (push, pop)
스택의 연산 (push, pop)

 

 

II. 스택의 정적/동적 구현

스택은 이전 챕터의 리스트와 마찬가지로 정적 또는 동적의 2가지 방법으로 구현될 수 있다.

 

1. 스택의 정적 구현 : 1차원 배열 사용

 

1) 스택의 생성

  • top : 가장 최근에 입력되었던 자료를 가리키는 변수
  • stack[0] ... stack[top] : 먼저 들어온 순으로 저장
  • 공백 상태: top = -1

배열을 이용한 스택의 구현1배열을 이용한 스택의 구현2
배열을 이용한 스택의 구현

// 1차원 배열의 사용

# define MAX_STACK_SIZE 100
int stack[MAX_STACK_SIZE];

/* top의 초기 값은 스택이 비어있을 때 -1 이다. */
int top = -1;

 

2) 스택의 삽입 (Push)

스택의 삽입(push)
스택의 삽입(push)

void push(int item)
{
    if (top >= MAX_STACK_SIZE - 1) {
    	stack_full();
        return;
    }
    	stack[++top] = item;
        // top ++; stack[top] = item;
}
// push(x)

if is_full(S)
	then error "overflow"
    	else top <- top + 1
    	     data[top] <- x

 

3) 스택의 삭제 (Pop)

스택의 삭제(pop)
스택의 삭제(pop)

int pop() {
    if (top == -1)
    	return stack_empty();
    // int x; x = stack[top]; top--; return x;
    return stack[top--];
}
// pop()

if is_empty()
    then error "underflow"
    else e <- data[top]
    	 top <- top-1
         return e

 

4) 스택의 검사 ( isempty / isfull )

// 스택이 비어있는지 검사하는 함수
int isempty()
{ if (top < 0)
    return(1);
  else
    return(0);
}
// 스택이 가득 차 있는지 검사하는 함수
int isfull()
{ if (top >= MAX_STACK_SIZE -1 )
    return(1);
  else
    return(0);
}

 

 

2. 스택의 동적 구현 : 연결 리스트 사용

- 장점: 크기가 제한되지 않음

- 단점: 구현이 복잡, 삽입/삭제 시간이 오래 걸림

 

1) 스택의 생성

// 요소의 타입
typedef int element;

typedef struct StackNode {
    // 노드의 타입
    element item;
    struct StackNode *link;
} StackNode; // 연결된 스택 관련 데이터

typedef struct {
    StackNode *top;
} LinkedStackType;

 

2) 스택의 삽입

// 삭제 함수
element pop(LinkedStackType *s)
{
    if( is_empty(s) ) {
    	fprintf(stderr, "스택이 비어있음\n");
        exit(1);
    }
    else{
        StackNode *temp = s->top;
        int item = temp->item;
        s->top = s->top -> link;
        free(temp);
        return item;
    }
}

 

3) 스택의 삭제

// 삭제 함수
element pop(LinkedStackType *s)
{
    if( is_empty(s) ) {
        fprintf(stderr, "스택이 비어있음\n");
        exit(1);
    }
    else{
    	StackNode *temp = s->top;
        int item = temp->item;
        s->top = s->top->link;
        free(temp);
        return item;
    }
}

 

 


 

[참조 문헌/자료]

- C언어로 쉽게 풀어쓴 자료구조 (개정3판)

반응형