Data Structures & Algorithms
[Data Structures] 스택 (Stack)
숄구-ml
2022. 5. 6. 18:48
1. 스택이란
- 선형 자료구조의 일종
- 한쪽은 막히고 한쪽은 뚫려있는 초코볼 통에 비유할 수 있다
- 후입선출 = Last In, First Out! (LIFO)
2. 스택 ADT와 헤더 파일 정의
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
|
#ifndef __AB_STACK_H__
#define __AB_STACK_H__
#define TRUE 1
#define FALSE 0
#define STACK_LEN 100
typedef int Data;
typedef struct _arrayStack
{
Data stackArr[STACK_LEN];
int topIndex;
} ArrayStack;
typedef ArrayStack Stack;
void StackInit(Stack * pstack);
//스택의 초기화를 진행
//스택 생성 후 제일 먼저 호출되어야 할 함수
int SIsEmpty(Stack * pstack);
//스택이 빈 경우 TRUE(1), 그렇지 않은 경우 FALSE(0) 반환
void SPush(Stack * pstack, Data data);
//스택에 데이터 저장, 매개변수 data로 전달된 값을 저장
Data SPop(Stack * pstack);
//마지막에 저장된 요소를 삭제
//삭제된 데이터는 반환
//본 함수의 호출을 위해서는 데이터가 하나 이상 존재함이 보장되어야 한다
Data SPeek(Stack * pstack);
//마지막에 저장된 요소를 반환하되 삭제하지는 않는다
//본 함수의 호출을 위해서는 데이터가 하나 이상 존재함이 보장되어야
#endif
|
cs |
3. 스택의 배열 기반 구현
- 인덱스 0의 배열 요소가 '스택의 바닥'으로 정의된다
- 마지막에 저장된 데이터의 위치를 기억해야 한다 - topIndex
- 스택 구현 코드 예시
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
|
//"ArrayBaseStack.h"
#ifndef __AB_STACK_H__
#define __AB_STACK_H__
#define TRUE 1
#define FALSE 0
#define STACK_LEN 100
typedef int Data;
typedef struct _arrayStack
{
Data stackArr[STACK_LEN];
int topIndex;
} ArrayStack;
typedef ArrayStack Stack;
void StackInit(Stack * pstack);
//스택의 초기화를 진행
//스택 생성 후 제일 먼저 호출되어야 할 함수
int SIsEmpty(Stack * pstack);
//스택이 빈 경우 TRUE(1), 그렇지 않은 경우 FALSE(0) 반환
void SPush(Stack * pstack, Data data);
//스택에 데이터 저장, 매개변수 data로 전달된 값을 저장
Data SPop(Stack * pstack);
//마지막에 저장된 요소를 삭제
//삭제된 데이터는 반환
//본 함수의 호출을 위해서는 데이터가 하나 이상 존재함이 보장되어야 한다
Data SPeek(Stack * pstack);
//마지막에 저장된 요소를 반환하되 삭제하지는 않는다
//본 함수의 호출을 위해서는 데이터가 하나 이상 존재함이 보장되어야
#endif
//"ArrayBaseStack.c"
void StackInit(Stack * pstack)
{
pstack->topIndex = -1; //-1은 빈 상태를 가리킴
}
void SIsEmpty(Stack * pstack)
{
if(pstack->topIndex == -1)
return TRUE;
else
return FALSE;
}
void SPush(Stack * pstack, Data data)
{
pstack->topIndex += 1;
pstack->stackArr[pstack->topIndex] = data;
}
Data SPop(Stack * pstack)
{
int rIdx;
if (SIsEmpty(pstack))
{
printf("stack memory error!");
exit(-1);
}
rIdx = pstack->topIndex;
pstack->topIndex -= 1;
return pstack->stackArr[rIdx];
}
Data SPeek(Stack * pstack)
{
if(SIsEmpty(pstack))
{
printf("stack memory error!");
exit(-1);
}
return pstack->stackArr[pstack->topIndex];
}
//"ArrayBaseStackMain.c"
#include <stdio.h>
#include "ArrayBaseStack.h"
int main(void)
{
//Stack 생성 및 초기화
Stack stack;
StackInit(&stack);
//데이터 넣기
SPush(&stack, 1); SPush(&stack, 2);
//데이터 꺼내기
while(!SIsEmpty(&stack))
printf("%d ", SPop(&Stack));
return 0;
}
|
cs |
4. 스택의 연결리스트 기반 구현

- head 포인터를 가지고, 머리에 새로운 노드를 저장하는 연결리스트가 바로 스택
- 연결리스트 기반 스택 구현 코드 예시
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
70
71
72
73
74
75
76
77
78
79
80
81
82
|
//"ListBaseStack.h"
#ifndef __LB_STACK_H__
#define __LB_STACK_H__
#define TRUE 1
#define FALSE 0
typedef int Data;
typedef struct _node
{
Data data;
struct _node * next;
} Node;
typedef struct _listStack
{
Node * head;
} ListStack;
typedef ListStack Stack;
void StackInit(Stack * pstack);
int SIsEmpty(Stack * pstack);
void SPush(Stack * pstack, Data data);
Data SPop(Stack * pstack);
Data SPeek(Stack * pstack);
#endif
//"ListBaseStack.c"
#include <stdio.h>
#include <stdlib.h>
#include "ListBaseStack.h"
void StackInit(Stack * pstack)
{
pstack->head = NULL;
}
void SIsEmpty(Stack * pstack)
{
if (pstack->head == NULL)
return TRUE;
else
return FALSE;
}
void SPush(Stack * pstack, Data data)
{
Node * newNode = (Node*)malloc(sizeof(Node));
newNode->data = data;
newNode->next = pstack->head;
pstack->head = newNode;
}
Data SPop(Stack * pstack)
{
Node * delNode;
Data delData;
if(SIsEmpty(pstack))
{
printf("stack memory error!");
exit(-1);
}
delNode = pstack->head;
delData = pstack->head->data;
pstack->head = pstack->head->next;
free(delNode);
return delData;
}
Data SPeek(Stack * pstack)
{
if(SIsEmpty(pstack))
{
printf("stack memory error!");
exit(-1);
}
return pstack->head->data;
}
|
cs |
5. 스택 기반의 계산기 프로그램 구현
- 수식의 표기법을 이해해보자
- 중위표기법 => 5 + 2 / 7
- 전위표기법 => + 5 / 2 7
- 후위표기법 => 5 2 7 / +
- 중위표기법을 후위표기법으로 변환하는 방법
- Stack 쟁반에는 연산자를 옮긴다
- 연산자간의 우선순위를 비교해 행동한다
- 쟁반에 위치한 연산자의 우선순위가 높다면 -> 쟁반에 위치한 연산자를 꺼내서 변환된 수식이 위치할 자리로 옮긴다 / 그리고 새 연산자는 쟁반으로 옮긴다 (사칙연산의 경우 연산자의 우선순위가 동일하면, 먼저 등장한 연산자를 먼저 진행한다)
- 쟁반에 위치한 연산자의 우선순위가 낮다면 -> 쟁반에 위치한 연산자의 위에 새 연산자를 쌓는다




-
- 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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
//"InfixToPostfix.c"
void GetOpPrec(char op) //연산자의 연산 우선순위 정보를 반환
{
swith(op)
{
case '*':
case '/':
return 5; //가장 높은 연산의 우선순위
case '+':
case '-':
return 3;
case '(':
return 1; //가장 낮은 연산의 우선순위
}
return -1; //등록되지 않은 연산자임을 알림
}
int WhoPrecOp(char op1, char op2) //GetOpPrec 함수의 호출결과를 바탕으로 두 연산자의 우선순위를 비교하여 결과 반환하는 함수
{
int op1Prec = GetOpPrec(op1);
int op2Prec = GetOpPrec(op2);
if(op1Prec > op2Prec)
return 1;
else if(op1Prec < op2Prec)
return -1;
else
return 0; // op1과 op2의 우선순위가 같다면
}
void ConvToRPNExp(char exp[]) //중위표기법을 후위표기법으로 변경하는 함수
{
Stack stack; //stack 쟁반 만들기
StackInit(&stack);
int i, idx = 0
char tok;
int expLen = strlen(exp);
char * convExp = (char*)malloc(expLen+1); //변환된 수식을 담을 공간 마련
memset(convExp, 0, sizeof(char)*expLen+1); //할당된 배열을 0으로 초기화
for(i=0; i<expLen; i++)
{
tok = exp[i];
if(isdigit(tok)) //tok에 저장된 문자가 숫자인지 확인
{
convExp[idx++] = tok; //숫자이면 배열 convExp에 그냥 저장
}
else //숫자가 아닌 연산자라면
{
switch(tok)
{
case '(':
SPush(&stack, tok); //여는 소괄호라면 스택에 쌓는다
break;
case ')':
while(1)
{
popOp = SPop(&stack);
if (popOp == '(')
break;
convExp[idx++] = popOp;
}
break;
case '+': case '-':
case '*': case '/':
while(!SIsEmpty(&stack) && WhoPrecOp(SPeek(&stack), tok) >= 0)
convExp[idx++] = SPop(&stack);
SPush(&stack, tok);
break;
}
}
}
while(!SIsEmpty(&stack)) //스택에 남아있는 모든 연산자를
conExp[idx++] = SPop(&stack); //배열 convExp로 이동한다
strcpy(exp, convExp); //변환된 수식을 exp에 복사하고,
free(convExp); //convExp는 소멸시킨다
}
//후위 표기법으로 표현된 수식을 계산하는 프로그램 구현
//1. 피연산자는 무조건 스택으로 옮긴다
//2. 연산자를 만나면 스택에서 두개의 피연산자를 꺼내서 계산을 한다
//3. 계산결과는 다시 스택에 넣는다
int EvalRPNExp(char exp[])
{
Stack stack;
int expLen = strlen(exp);
int i;
char tok, op1, op2;
StackInit(&stack);
for (i=0; i<expLen; i++)
{
tok = exp[i];
if(isdigit(tok))
{
SPush(&stack, tok-'0') //정수라면 숫자로 변환 후 스택에 push
}
else
{
op2 = SPop(&stack);
op1 = SPop(&stack);
swith(tok)
{
case '+':
SPuch(&stack, op1+op2);
break;
case '-':
SPush(&stack, op1-op2);
break;
case '*':
SPush(&stack, op1*op2);
break;
case '/':
SPush(&stack, op1/op2);
break;
}
}
}
return SPop(&stack);
}
|
cs |
728x90