(구) 자료/C

[5] Stack

뜐뜐뜐 2018. 5. 6. 16:18

1.  스택(Stack)이란?

- 더미, 낟가리라는 사전적 의미를 갖고 있음.

- 실생활의 예로는 식당의 접시 더미, 책상에 쌓인 책, 창고에 쌓인 상자 등임.

- 후입 선출(LIFO : Last In First Out)의 입출력 형태를 갖고 있음.


2. 전형적인 스택의 사용 예

- 함수 호출에서 복귀 주소를 기억하는 데에 스택을 사용함.

3. 스택의 구현 방법


 

 Array

Linked List


장점

 구현 간단

스택 크기 자유자재


단점

 스택 크기 고정

구현 복잡




4. 스택 구현(Code)

1) 단일 element를 저장하는 Stack

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
#include<stdio.h>
#include<stdlib.h>
#define MAX_STACK_SIZE 100
 
typedef int element;
 
element stack[MAX_STACK_SIZE];
int top = -1;
 
int is_empty() {
    return (top == -1);
}
 
int is_full() {
    return (top == (MAX_STACK_SIZE - 1));
}
 
void push(element item) {
    if (is_full()) {
        fprintf(stderr, "스택 꽉참\n");
        return;
    }
    else stack[++top] = item;
}
 
element pop() {
    if (is_empty()) {
        fprintf(stderr, "스택 비어있음\n");
        exit(1);
    }
    else return stack[top--];
}
 
element peek() {
    if (is_empty()) {
        fprintf(stderr, "스택 비어있음\n");
        exit(1);
    }
    else return stack[top];
}
 
void main() {
    for (int i = 1; i <= 3; i++)
        push(i);
 
    for (int i = 0; i < 4; i++)
        printf("%d\n", pop());
}
 
cs

- 이 방법은 프로그램에서 하나 이상의 스택을 사용해야 할 때, 상당히 곤란해질 수 있음

- 전역 변수를 사용하는 것은, 프로그램을 복잡하게 만들어서 오류가 발생할 확률을 높임.


2) 여러가지 element를 갖는 Struct 스택

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
59
60
61
62
63
64
65
66
67
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
 
#define MAX_STACK_SIZE 100
#define MAX_STRING 100
 
typedef struct element{
    int student_no;
    char name[MAX_STRING], address[MAX_STRING];
}element;
 
element stack[MAX_STACK_SIZE];
 
int top = -1;
 
int is_empty() {
    return (top == -1);
}
 
int is_full() {
    return (top == (MAX_STACK_SIZE - 1));
}
 
void push(element item) {
    if (is_full()) {
        fprintf(stderr, "스택 꽉참\n");
        return;
    }
    else stack[++top] = item;
}
 
element pop() {
    if (is_empty()) {
        fprintf(stderr, "스택 비어있음\n");
        exit(1);
    }
    else return stack[top--];
}
 
element peek() {
    if (is_empty()) {
        fprintf(stderr, "스택 비어있음\n");
        exit(1);
    }
    else return stack[top];
}
 
int main() {
 
    element ie, oe;
 
    strcpy(ie.name, "홍길동");
    strcpy(ie.address, "서울");
    ie.student_no = 20053001;
 
    push(ie);
 
    oe = pop();
 
    printf("name : %s\n", oe.name);
    printf("address : %s\n", oe.address);
    printf("student number : %d\n", oe.student_no);
 
    return 0;
}
cs

- 단점이 있다면, stack 배열과 top이 전역 변수로 선언되기 때문에 전체 프로그램에서 여러 개의 스택을 사용하기가 어렵다는 점임.

3) 관련 데이터를 합수의 매개 변수로 전달하는 방법.

- 위의 단점을 보완해줄 방법.


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
59
#include<stdio.h>
#include<stdlib.h>
 
#define max_stack_size 100
 
typedef int element;
typedef struct {
    element stack[max_stack_size];
    int top;
}StackType;
 
void init(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->stack[++(s->top)] = item;
}
 
element pop(StackType *s) {
    if (is_empty(s)) {
        fprintf(stderr, "스택 공백\n");
        exit(1);
    }
    else return s->stack[(s->top)--];
}
 
element peek(StackType *s) {
    if (is_empty(s)) {
        fprintf(stderr, "스택 공백\n");
        exit(1);
    }
    else return s->stack[s->top];
}
 
int main() {
 
    StackType s;
 
    init(&s);
 
    for (int i = 1; i <= 3; i++)
        push(&s, i);
    for (int i = 1; i < 5; i++
        printf("%d\n", pop(&s));
    return 0;
}
cs

- 위의 코드가 복잡한 이유는 구조체의 포인터를 각 함수에 전달해야되는 점 때문임.

- 각 함수에서는 구조체의 포인터를 이용해서 스택을 조작함.

- C언어에서 함수 매개 변수 전달 방식이 기본적으로 call by value라서, 그냥 구조체만 전달할 경우 원본이 아니라 복사본이 전달되는 것.

- 그래서 함수 내부에서 값을 바꿔도, 파라미터로 들어온 값에는 영향을 미치지 못한다.

- 그러나 원본에 대한 포인터를 전달하면 원본을 변경할 수 있음.

- 그치만, 구조체 복사에 따른 부담이 고려되므로 주의.

- 이 방법은 여러 개의 스택을 만들 수 있다는 큰 장점을 갖고 있음.


4) 링크드리스트로 만든 스택

- 메모리상의 제한을 해결한 스택이지만, 삽입과 삭제에는 시간이 조금 걸린다.

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
59
60
61
62
63
64
65
66
67
68
69
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
 
typedef int element;
typedef struct StackNode {
    element item;
    StackNode *link;
}StackNode;
 
typedef struct {
    StackNode *top;
}LinkedStackType;
 
void init(LinkedStackType *s) {
    s->top = NULL;
}
 
int is_empty(LinkedStackType *s) {
    return (s->top == NULL);
}
 
void push(LinkedStackType *s, element item) {
    StackNode *temp = (StackNode*)malloc(sizeof(StackNode));
    if (temp == NULL) {
        fprintf(stderr, "메모리 할당 에러\n");
        return;
    }
    else {
        temp->item = item;
        temp->link = s->top;
        s->top = temp;
    }
}
element pop(LinkedStackType *s) {
    if (is_empty(s)) {
        fprintf(stderr, "스택 공백\n");
        exit(1);
    }
    else {
        // top을 임시로 가리킬 temp
        StackNode *temp = s->top;
        // pop할 item을 임시 저장
        element item = temp->item;
        // top은 한 칸 뒤로감
        s->top = s->top->link;
        free(temp);
        return item;
    }
}
 
element peek(LinkedStackType *s) {
    if (is_empty(s)) {
        fprintf(stderr, "스택 공백\n");
        exit(1);
    }
    else return s->top->item;
}
 
int main() {
    LinkedStackType s;
    init(&s);
 
    for (int i = 0; i < 3; i++)
        push(&s, i);
    for (int i = 0; i < 5; i++
        printf("%d\n", pop(&s));
    
}
cs


5. 스택의 응용

- 괄호 검사 프로그램, 중위, 전위, 후위 표기 프로그램, 미로찾기(DFS)