이 내용은 윤성우의 열혈 자료구조를 학습한 내용입니다.
기본적으로 스택에서는 데이터를 넣고(push), 꺼내고(pop), 들여다 보는(peek) 연산을 진행한다.
void StackInit(Stack * pstack)int SIsEmpty(Stack * pstack)void SPush(Stack * pstack, Data data) : pushData SPop(Stack * pstack) : popData SPeek(Stack * pstack) : peek스택은 배열 혹은 연결리스트 기반으로 구현이 가능하다. 이들 모두 구현할 예정이다.
구현의 논리

topInexpush : Top을 위로 한 칸 올리고, Top이 가리키는 위치에 데이터 저장pop : Top이 가리키는 데이터를 반환하고, Top을 아래로 한 칸 내림스택의 헤더파일
#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);
void SPush(Stack * pstack, Data data);
Data SPop(Stack * pstack);
Data SPeek(Stack * pstack);
#endif
typedef struct _arrayStack
{
Data stackArr[STACK_LEN]; // typedef int Data;
int topIndex;
} ArrayStack;topIndex 하나이다.void StackInit(Stack * pstack)
{
pstack->topIndex = -1; // topInex의 -1은 스택이 비었음을 의미한다.
}int SIsEmpty(Stack * pstack)
{
if(pstack->topIndex == -1) // 스택이 비어있다면
return TRUE;
else
return FALSE;
}push, pop, peekvoid 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; // pop 연산의 결과로 topIndex 값 하나 감소
return pstack->stackArr[rIdx]; // 삭제되는 데이터 반환
}topInex 값을 근거로 데이터를 저장하기 때문에, 해당 값을 하나 감소시키는 것 만으로도 데이터의 소멸은 완성된다.Data SPeek(Stack * pstack)
{
if(SIsEmpty(pstack))
{
printf("Stack Memory Error!");
exit(-1);
}
return pstack->stackArr[pstack->topIndex]; // 맨 위에 저장된 데이터 반환
}#include <stdio.h>
#include "ArrayBaseStack.h"
int main(void)
{
/*** Stack의 생성 및 초기화 ***/
Stack stack;
StackInit(&stack);
/*** 데이터 넣기 ***/
SPush(&stack, 1); SPush(&stack, 2);
SPush(&stack, 3); SPush(&stack, 4);
SPush(&stack, 5);
/*** 데이터 꺼내기 ***/
while(!SIsEmpty(&stack))
printf("%d ", SPop(&stack));
return 0;
}
5 4 3 2 1
#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);
#endifvoid StackInit(Stack * pstack)
{
pstack->head = NULL; // 포인터 변수 head는 NULL로 초기화한다.
}int SIsEmpty(Stack * pstack)
{
if(pstack->head == NULL) // 스택이 비면 head에는 NULL이 저장된다.
return TRUE;
else
return FALSE;
}push, pop, peekvoid SPush(Stack * pstack, Data data)
{
Node * newNode = (Node*)malloc(sizeof(Node)); // 새 노드 생성
newNode->data = data; // 새 노드에 데이터 저장
newNode->next = pstack->head; // 새 노드가 최근에 추가된 노드를 가리킴
pstack->head = newNode; // 포인터 변수 head가 새 노드를 가리킴
}Data SPop(Stack * pstack)
{
Data rdata;
Node * rnode;
if(SIsEmpty(pstack)) {
printf("Stack Memory Error!");
exit(-1);
}
rdata = pstack->head->data; // 삭제할 노드의 데이터를 임시로 저장
rnode = pstack->head; // 삭제할 노드의 주소 값을 임시로 저장
pstack->head = pstack->head->next; // 삭제할 노드의 다음 노드를 head가 가리킴
free(rnode); // 노드 삭제
return rdata; // 삭제된 노드의 데이터 반환
}free)시키고, 소멸된 노드의 데이터(rdata)를 반환Data SPeek(Stack * pstack)
{
if(SIsEmpty(pstack)) {
printf("Stack Memory Error!");
exit(-1);
}
return pstack->head->data; // head가 가리키는 노드에 저장된 데이터 반환
}malloc: void *malloc(size_t size);
int *pi = (int *)malloc(sizeof(int)); //원하는 형식 포인터로 형변환
printf("초기: %d \n",*pi); // 초기: -842150451 (쓰레기 값)
free: void free(void *memblock);
free(pi); //더 이상 필요없을 때 해제
#include <stdio.h>
#include "ListBaseStack.h"
int main(void)
{
/*** Stack의 생성 및 초기화 ***/
Stack stack;
StackInit(&stack);
/*** 데이터 넣기 ***/
SPush(&stack, 1); SPush(&stack, 2);
SPush(&stack, 3); SPush(&stack, 4);
SPush(&stack, 5);
/*** 데이터 꺼내기 ***/
while(!SIsEmpty(&stack))
printf("%d ", SPop(&stack));
return 0;
}
5 4 3 2 1수식을 이루는 피연산자는 한자리 숫자로만 이뤄진다고 가정한다.
5 + 2 / 7+ 5 / 2 75 2 7 / +5 2 4 op1 op2 : op1 연산을 하고 나서 op2 연산을 진행위 두 과정을 기준으로 각각의 알고리즘을 작성해 보도록 하자.
후위 표기법에서 우선순위가 높은 연산자가 변환된 수식의 앞부분에 위치해야 한다.

기본 과정은 소괄호가 없을 경우와 같다. 다만, ( 와 ) 연산자의 수행은 다르게 진행된다.
( 연산자) 연산자( 이후에 스택에 쌓인 연산자들을 변환된 수식의 자리로 옮긴다.) 연산자는 메시지만 취하고 버린다.
int GetOpPrec(char op) // 연산자의 연산 우선순위 정보를 반환
{
switch(op)
{
case '*':
case '/':
return 5; // 가장 높은 연산 우선순위
case '+':
case '-':
return 3; // 중간 순위
case '(':
return 1; // 가장 낮은 연산의 우선순위
}
return -1; // ')': 등록되지 않은 연산자임을 알림
}int WhoPrecOp(char op1, char op2)
{
int op1Prec = GetOpPrec(op1);
int op2Prec = GetOpPrec(op2);
if(op1Prec > op2Prec) // op1의 우선순위가 더 높다면
return 1;
else if(op1Prec < op2Prec) // op2의 우선순위가 더 높다면
return -1;
else
return 0; // op1, op2의 우선순위가 같다면
}void ConvToRPNExp(char exp[]) // 후위 표기법의 수식으로 변환
{
Stack stack;
int expLen = strlen(exp);
char * convExp = (char*)malloc(expLen+1); // 변환된 수식을 담을 공간 마련
int i, idx=0;
char tok, popOp;
memset(convExp, 0, sizeof(char)*expLen+1); // 할당된 배열을 0으로 초기화
StackInit(&stack);
for(i=0; i<expLen; i++)
{
tok = exp[i]; // exp로 전달된 수식을 한 문자씩 tok에 저장
if(isdigit(tok)) // tok에 저장된 문자가 숫자인지 확인
{
convExp[idx++] = tok;
}
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))
convExp[idx++] = SPop(&stack);
strcpy(exp, convExp); // 변환된 수식을 exp에 복사
free(convExp); // convExp 소멸
}void * memset(void *ptr, int val, size_t len);ptr 로 전달된 주소의 메모리서부터 len 바이트를 val 의 값으로 채운다.int isdigit(int ch);ch로 전달된 문자의 내용이 10진수라면 1을 반환한다.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); // 첫 번째 피연산자
switch(tok) // 연산결과를 다시 스택에 push
{
case '+':
SPush(&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); // 마지막 연산결과를 스택에서 꺼내어 반환
}
tok - '0' 으로 정수 값을 알 수 있다.참고: 윤성우의 열혈 자료구조