자료 구조 - 스택
‘c++로 쉽게 풀어쓴 자료구조 1판 - 천인국, 최영규’ 책을 참고하여 작성한 포스트입니다.
스택
스택이란 자료의 입출력이 후입선출(LIFO:Last-In First-Out)의 형태로 일어나는 자료구조를 말한다.
- 스택 상단(stack top): 스택에서 입출력이 이루어지는 부분
- 스택 하단(stack bottom): top의 반대 부분
- 요소(element): 스택에 저장되는 것, 항목
- 공백(empty)상태: 스택에 요소가 하나도 없는 상태
- 포화(full)상태: 스택에 더 이상 요소를 넣을 수 없는 상태
스택의 추상 자료형
데이터: 후입선출의 접근 방법을 유지하는 요소들의 모음
연산
- push(x): 주어진 요소x를 스택의 맨 위에 추가
- pop(): 스택이 비어있지 않으면 맨 위에 있는 요소를 삭제하고 반환
- isEmpty(): 스택이 비어있으면 true, 그렇지 않으면 false 반환
- peek(): 스택이 비어있지 않으면 맨 위에 있는 요소를 삭제하지 않고 반환
- isFull(): 스택이 가득 차 있으면 true, 그렇지 않으면 false 반환
- size(): 스택 내의 모든 요소들의 개수를 반환
- display(): 스택 내의 모든 요소들을 출력함
스택의 구현
- 스택의 구현 방법으로 배열 혹은 연결 리스트를 사용할 수 있다.
- 여기선 배열로 구현하고 연결 리스트는 나중에!
배열을 이용한 스택의 표현
배열은 순차적인 메모리 공간에 할당된다고 해서 순차적 표현(sequential representation)이라고도 한다. 배열은 같은 자료형의 변수를 여러 개 만드는 경우에 특히 유용하고, 항목을 저장할 수 있는 여러 개의 공간을 제공한다. 각 공간은 정확히 하나의 항목만을 담으며 각 항목들은 인덱스 번호를 통해 직접 접근이 가능하다. 인덱스 번호는 0부터 시작한다.
직접 구현해서 책과 내용이 다를 수 있습니다
구현 전!
// 스택의 상단을 나타내는 인덱스
int top = -1;
// 스택을 구현할 배열의 사이즈
int MAX_STACK_SIZE = 10;
// 스택을 구현할 배열
int stack[10];
// 에러 처리를 메세지 출력으로 대신함
void printErrorMSG(string s) {
printf("%s", s);
}
push(x) 연산
void push(x) {
if (top == MAX_STACK_SIZE - 1) {
printErrorMSG("overflow");
} else {
//비어 있는 배열의 자리로 top 인덱스를 옮기고
//그 위치에 x를 대입
stack[++top] = x;
}
}
pop() 연산
int pop() {
if(top == -1) { // 스택이 비어 있는 경우
printErrorMSG("Stack is empty!");
} else {
//스택의 상단을 가리키는 원소 반환 후
//top 인덱스를 1만큼 감소
return stack[top--];
}
}
isEmpty() 연산
bool isEmpty() {
// 비어 있을 때(초기화한 경우도 마찬가지)
// 스택 상단의 인덱스는 -1로 정의했다
if (top == -1) {
return true;
} else {
return false;
}
}
peek() 연산
int peek() {
if(top == -1) { // 스택이 비어 있는 경우
printErrorMSG("Stack is empty!");
} else {
// 스택 상단의 원소 반환
return stack[top];
}
}
isFull() 연산
bool isFull() {
if(top == MAX_STACK_SIZE - 1) {
return true;
} else {
return false;
}
}
size() 연산
int size() {
// 배열의 인덱스는 0에서 시작하므로
return top+1;
}
display() 연산
void display() {
for(int i = 0; i <= top; i++) {
printf("stack의 %d번째 요소는 %d입니다.\n", i, stack[i]);
}
}
수식의 계산
후위 표기법을 이용해 사칙연산을 구현한다. 후기 표기법은 스택과 잘 맞는 표기법이고, 괄호나 사칙연산의 우선순위를 고려하지 않아도 되는 장점이 있다.
사칙 연산 기호들은 char형이지만 int, double 등으로 자동 형변환 된다.
- ArrayStack은 위에서 구현한 Stack .
중위 표기법을 후기 표기법으로 변환
void convert(char* ch, int len, char* ch2) {
ArrayStack arrayStack;
int idx = 0;
for(int i=0; i<len; ++i) {
if(ch[i] == '+' || ch[i] == '-' || ch[i] == '*'
|| ch[i] == '/' || ch[i] == '(' || ch[i] == ')') {
switch (ch[i]) {
case '+' : case '-' : case '*' : case '/' :
if(arrayStack.peek() == '*' || arrayStack.peek() == '/') {
ch2[idx++] = arrayStack.pop();
}
arrayStack.push(ch[i]);
break;
case '(' :
arrayStack.push(ch[i]);
break;
case ')' :
while(!arrayStack.isEmpty()) {
double d = arrayStack.pop();
if(d != '(') ch2[idx++] = d;
else break;
}
break;
}
} else {
ch2[idx++] = ch[i];
}
}
while(!arrayStack.isEmpty()) {
ch2[idx++] = arrayStack.pop();
}
}
후위 표기법 계산
double calculate(char* ch, int len) {
ArrayStack arrayStack;
for(int i=0; i<len; ++i) {
char c = *(ch + i);
if (c == '+' || c == '-' || c == '*' || c == '/') {
double right = arrayStack.pop();
double left = arrayStack.pop();
switch(c) {
case '+' :
arrayStack.push(left + right);
break;
case '-' :
arrayStack.push(left - right);
break;
case '*' :
arrayStack.push(left * right);
break;
case '/' :
arrayStack.push(left / right);
break;
}
} else {
arrayStack.push(c - '0');
}
}
return arrayStack.pop();
}
확인
int main()
{
char ch[10] = "82/3-32*+";
cout << calculate(ch, strlen(ch))<< endl;
char ch2[12] = "8*2-3*(3+2)";
char ch3[12];
convert(ch2, strlen(ch2), ch3);
for(int i=0; i< strlen(ch3); ++i) {
cout << ch3[i] << " ";
}
cout << endl;
cout << calculate(ch3, strlen(ch3)) << endl;
return 0;
}
/* print
7
stack is empty
stack is empty
8 2 * 3 3 2 + * -
1
*/
Leave a comment