스택
2021. 6. 18. 21:10ㆍ자료구조
728x90
스택
정의
- 쌓아놓은 더미
구조
- LIFO 구조 (Last - In - First - Out) : 가장 최근의 데이터가 가장 먼저 나감
- pop() : 스택에 데이터를 삭제
- push () : 스택에 데이터를 추가
예제
- 스택 추상데이터타입(ADT)
-
- create(size) ::= 최대 크기가 size인 공백 스택을 생성한다.
- is_full(s) ::= if(스택의 원소수 == size) return TRUE;
- else return FALSE;
- is_empty(s) ::= if(스택의 원소수 == 0) return TRUE;
else return FALSE; - push(s, item) ::= if(is_full(s)) return ERROR_STACKFULL;
else 스택의 맨 위에 item을 추가한다 - pop(s) ::= if(is_empty(s)) return ERROR_STACKEMPTY;
else 스택의 맨 위의 원소를 제거해서 반환한다 - peek(s) ::= if(is_empty(s)) return ERROR_STACKEMPTY;
else 스택의 맨 위의 원소를 제거하지 않고 반환한다
구현(스스로 해보기)
#define _CRT_SECURE_NO_WARNINGS
#define MAX_STACK_SIZE 100
#define ERROR_STACKFULL -1
#define ERROR_STACKEMPTY -2
/*
error 목록
return -1 : ERROR_STACKFULL
return -2 : ERROR_STACKEMPTY
*/
#include <stdio.h>
typedef int element;
element stack[MAX_STACK_SIZE]; // stack 생성
int idx = -1;
int item = 0;
int is_full() {
if (idx + 1 == MAX_STACK_SIZE)
return 1;
else
return 0;
}
int is_empty() {
if (idx == -1)
return 1;
else
return 0;
}
int push(element *s, int item) {
if (is_full())
return ERROR_STACKFULL;
else {
s[idx++] = item;
return 1;
}
}
int pop(element* s) {
printf("%d\n", idx);
if (is_empty())
return ERROR_STACKEMPTY;
else s[idx--];
}
int peek(element* s) {
return s[idx];
}
int main(void) {
push(stack, 1);
push(stack, 2);
printf("%d", pop(stack));
}
🤔문제점
- pop이 제대로 실행 안됨
- 굳이 포인터를 사용해서 전역변수에 접근할 필요가 없었음
- return 값 때문에 쓸데없는 반환 및 코드가 늘어남.
구현 (정답 코드)
#include <stdio.h>
#include <stdlib.h>
#define MAX_STACK_SIZE 100 // 스택의 최대 크기
typedef int element; // 데이터의 자료형
element stack[MAX_STACK_SIZE]; // 1차원 배열
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(void)
{
push(1);
push(2);
push(3);
printf("%d\n", pop());
printf("%d\n", pop());
printf("%d\n", pop());
return 0;
}
🤔참고할 점
- fprintf를 사용하여 함수 내에서 바로 에러 처리를 함 -> 코드의 간략화 및 순서도의 최적화
- stack full과 stack empty를 판단하는 함수는 바로 비교연산을 리턴함으로서 훨씬 직관적이면서 간략한 코드가 완성됨
- 전역으로 선언한 stack을 굳이 포인터로 참조하여 사용하지 않음.
스택 응용
구조체 스택(스스로 해보기)
#define _CRT_SECURE_NO_WARNINGS
#define MAX_STACK_SIZE 100
#include <stdio.h>
#include <stdlib.h>
typedef int element;
typedef struct {
element data[MAX_STACK_SIZE];
int top;
}StackType;
void init_stack(StackType* s) {
/*for (int i = 0; i < MAX_STACK_SIZE; i++) {
s->data[i] = NULL;
}*/
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, "STACK IS FULL!");
exit(-1);
}
else {
s->data[++(s->top)] = item;
}
}
element pop(StackType* s) {
if (is_empty(s)) {
fprintf(stderr, "STACK IS EMPTY!");
exit(-1);
}
else {
element re = s->data[s->top];
s->data[s->top] = NULL;
(s->top)--;
return re;
}
}
int main(void) {
StackType Stack;
init_stack(&Stack);
for (int i = 0; i < 100; i++) {
push(&Stack, i);
printf("push [%d] -> %d\n", Stack.top, Stack.data[i]);
}
printf("=====================================\n");
for (int i = 0; i < 100; i++) {
printf("pop [%d] -> %d\n",Stack.top, pop(&Stack));
}
}
구조체와 구조체 포인터를 이용하여 스택을 구현함
앞선 예제를 응용하여 정답 코드와 근접한 코드를 구현해낼 수 있었음
정답코드와 다른 점은 init과 pop에서 비어있는 구조체 멤버 data의 요소를 NULL로 초기화 시켜줌
구조체 스택 (정답 코드)
#include <stdio.h>
#include <stdlib.h>
// ===== 스택 코드의 시작 =====
#define MAX_STACK_SIZE 100
typedef int element;
typedef struct {
element data[MAX_STACK_SIZE];
int top;
} StackType;
// 스택 초기화 함수
void init_stack(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->data[++(s->top)] = item;
}
// 삭제함수
element pop(StackType* s)
{
if (is_empty(s)) {
fprintf(stderr, "스택 공백 에러\n");
exit(1);
}
else return s->data[(s->top)--];
}
// 피크함수
element peek(StackType* s)
{
if (is_empty(s)) {
fprintf(stderr, "스택 공백 에러\n");
exit(1);
}
else return s->data[s->top];
}
// ===== 스택 코드의 끝 =====
int main(void)
{
StackType Stack;
init_stack(&Stack);
for (int i = 0; i < 100; i++) {
push(&Stack, i);
printf("push [%d] -> %d\n", Stack.top, Stack.data[i]);
}
printf("=====================================\n");
for (int i = 0; i < 100; i++) {
printf("pop [%d] -> %d\n", Stack.top, pop(&Stack));
}
}
함수 원형을 참고하여 코드를 작성하였음
peek 함수는 따로 구현되어있지 않은데 pop을 응용하여 쉽게 구현 가능
스택 응용 2
동적 배열 스택 (스스로 해보기)
#define _CRT_SECURE_NO_WARNINGS
#define MAX_STACK_SIZE 100
#include <stdio.h>
#include <stdlib.h>
typedef int element;
typedef struct {
element *data;
int capacity;
int top;
}StackType;
void init_stack(StackType* s) {
s->top = -1;
s->capacity = 1;
s->data = (element*)malloc(s->capacity * sizeof(element));
}
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)) {
s->capacity *= 2; //2배로 늘리는게 부하 관련해서 훨씬 경제적임
s->data = (element*)realloc(s->data, s->capacity * sizeof(element));
}
s->data[++(s->top)] = item;
}
element pop(StackType* s) {
if (is_empty(s)) {
fprintf(stderr, "Error - STACK IS EMPTY!");
exit(-1);
}
return s->data[(s->top)--];
}
int main(void) {
StackType* Stack;
init_stack(&Stack);
push(&Stack, 1);
push(&Stack, 2);
push(&Stack, 3);
printf("%d \n", pop(&Stack));
printf("%d \n", pop(&Stack));
printf("%d \n", pop(&Stack));
free(Stack->data);
return 0;
}
동적 배열 스택 (정답 코드)
#include <stdio.h>
#include <stdlib.h>
#define MAX_STACK_SIZE 100
typedef int element;
typedef struct {
element* data; // data은 포인터로 정의된다.
int capacity; // 현재 크기
int top;
} StackType;
// 스택 생성 함수
void init_stack(StackType* s)
{
s->top = -1;
s->capacity = 1;
s->data = (element*)malloc(s->capacity * sizeof(element));
}
// 공백 상태 검출 함수
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)) {
s->capacity *= 2;
s->data =
(element*)realloc(s->data, s->capacity * sizeof(element));
}
s->data[++(s->top)] = item;
}
// 삭제함수
element pop(StackType* s)
{
if (is_empty(s)) {
fprintf(stderr, "스택 공백 에러\n");
exit(1);
}
else return s->data[(s->top)--];
}
int main(void)
{
StackType s;
init_stack(&s);
push(&s, 1);
push(&s, 2);
push(&s, 3);
printf("%d \n", pop(&s));
printf("%d \n", pop(&s));
printf("%d \n", pop(&s));
free(s.data);
return 0;
}
:bulb: 두 코드 모두 마지막에 디버깅 예외로 인하여 오류 후 종료됨.
:bulb: 스스로 작성한 코드의 마지막 free()에는 구조체 요소에 화살표 연산자 (->)로 접근하지 않으면 안되었는데
정답코드는 . 으로 연결하였음.. 두 차이가 궁금함..
느낀점
이론 자체는 어렵지 않은 부분이었으나, 구현하는데 상당히 복잡하게 하였음. 생각을 조금 더 "프로그래머" 적으로 하여 효율적인 코드를 짜도록 노력해야 할 것 같음.
728x90
'자료구조' 카테고리의 다른 글
후위표기식 전환 (스택) 2 (0) | 2021.06.18 |
---|---|
후위표기식 전환 (스택이용) 1 (0) | 2021.06.18 |
큐 (0) | 2021.06.18 |
C++STL (0) | 2020.10.16 |
[C 자료구조] 자료구조와, 알고리즘 성능 분석 방법 (0) | 2020.05.30 |