-> 블로그 이전

[Data Structure] 스택 (Stack)

2021. 11. 30. 21:15Major`/자료구조

스택이란?

"LIFO 구조" : Last-In First-Out

  • 프링글스 과자라고 생각하면 된다
  • 프링글스는 제조 과정에서 과자들을 차례대로 통에 넣어준다
  • 당연히 프링글스를 사먹는 사람들은 일반적으로 제조과정에서 마지막에 넣은 과자를 먼저 먹게된다

이처럼 스택은 몇몇 요소들을 넣으면 가장 나중에 넣은 요소들부터 차레대로 pop이 되는 구조이다

 

스택 ADT (추상 자료형)

- n개의 element형의 요소들의 선형 리스트

boolean is_full(stack *s)

  • 스택 포화 상태 검출 함수

boolean is_empty(stack *s)

  • 스택 공백 상태 검출 함수

void push(stack *s, element item)

  • 스택 s에 item을 삽입

element pop(stack *s)

  • 스택 s의 가장 상단에 있는 element return 후 스택에서 삭제

element peek(stack *s)

  • 스택 s의 가장 상단에 있는 element return
  • pop과는 달리 스택에서 삭제하지 않는다

배열로 구현한 스택

스택 구조체 정의 (Array)

#define MAX_STACK_SIZE 100

typedef int element;

typedef struct {
	element stack[MAX_STACK_SIZE];
	int top;
} Stack;

void init_stack(Stack* s) { 
	// 스택 초기화 함수 (공백상태이면 top = -1)
	s->top = -1;
}
  • 스택에는 항상 마지막으로 들어온 element를 가리키는 SP(Stack Pointer)가 존재한다
  • SP를 int top으로 조절하면서 top 변수는 항상 스택의 마지막 element의 index를 가리키게 해준다

 

1. is_full / is_empty

int is_full(Stack* s) { 
	// 스택이 가득 차있으면 가장 상단의 인덱스 = MAX_STACK_SIZE -1
	return s->top == (MAX_STACK_SIZE)-1;
}

int is_empty(Stack* s) { 
	// 공백상태이면 top = -1, 그러므로 return top == -1
	return s->top == -1;
}

- is_full

  • 스택이 가득 차있으면 더이상 push(삽입) 불가
  • 가득 차있으면 스택의 최상단 인덱스 = 스택의 최대크기 - 1 

- is_empty

  • 스택이 비어있으면 pop(삭제), peek(선택) 불가
  • 비어있으면 스택의 top = -1 (공백상태)

 

 

2. push

void push(Stack* s, element item) { // 스택의 top에 새로운 item을 삽입하는 함수
	// 스택이 꽉 찼으면 push 불가, 스택 top을 1 증가시키고 1 증가시킨 인덱스에 item push
	if (is_full(s)) {
		fprintf(stderr, "스택이 꽉 찼습니다!!\n");
		return;
	}
	else s->stack[++(s->top)] = item;
}
  • 스택이 가득 차있으면 error발생
  • push를 하려면 top을 1 증가시켜야하고 이 증가시킨 배열에 item을 추가해야 한다
  • 그러므로 ++(s->top)을 통해서 top을 1증가시키고
  • 증가시킨 배열 s-stack[++(s->top)]에 item을 추가

 

 

3. pop

element pop(Stack* s) { // 스택의 top에 있는 item을 꺼내고 스택에서 삭제시키는 함수
	// 스택이 비어있으면 pop불가, 스택에서 top에 있는 배열item을 꺼내고 top을 1 감소
	if (is_empty(s)) {
		fprintf(stderr, "스택이 공백입니다!!\n");
		return;
	}
	else return s->stack[(s->top)--]; // 스택을 꺼내고 top을 1 감소
}
  • 스택의 top에 존재하는 item을 꺼냄으로써, top은 1 감소된다
  • s->stack[(s->top)--]에서
  • 먼저 s->top에 해당하는 item을 return시키고
  • 이다음에 (s->top)--를 통해 top을 1 감소시킨다

 

 

4. peek

element peek(Stack* s) { // top에 있는 item 보여주는 함수
	// 스택이 비어있으면 peek 불가, 스택에서 top에 있는 item을 return
	if (is_empty(s)) {
		fprintf(stderr, "스택이 공백입니다!!\n");
		return;
	}
	else return s->stack[s->top];
}
  • 그냥 스택의 top에 있는 item을 보여주기만 하기 때문에 item이 삭제되거나 top이 바뀌지 않는다

 

 

5. Full Code (Stack : Array)

#include <stdio.h>
#include <stdlib.h>
// 배열로 구현한 스택

#define MAX_STACK_SIZE 100

typedef int element; // 스택 각각의 item

typedef struct {
	element stack[MAX_STACK_SIZE];
	int top; // 스택의 top, 공백상태면 -1
} Stack;

void init_stack(Stack* s) { 
	// 스택 초기화 함수 (공백상태이면 top = -1)
	s->top = -1;
}

int is_full(Stack* s) { 
	// 스택이 가득 차있으면 가장 상단의 인덱스 = MAX_STACK_SIZE -1
	return s->top == (MAX_STACK_SIZE)-1;
}

int is_empty(Stack* s) { 
	// 공백상태이면 top = -1, 그러므로 return top == -1
	return s->top == -1;
}

void push(Stack* s, element item) { // 스택의 top에 새로운 item을 삽입하는 함수
	// 스택이 꽉 찼으면 push 불가, 스택 top을 1 증가시키고 1 증가시킨 인덱스에 item push
	if (is_full(s)) {
		fprintf(stderr, "스택이 꽉 찼습니다!!\n");
		return;
	}
	else s->stack[++(s->top)] = item;
}

element pop(Stack* s) { // 스택의 top에 있는 item을 꺼내고 스택에서 삭제시키는 함수
	// 스택이 비어있으면 pop불가, 스택에서 top에 있는 배열item을 꺼내고 top을 1 감소
	if (is_empty(s)) {
		fprintf(stderr, "스택이 공백입니다!!\n");
		return;
	}
	else return s->stack[(s->top)--]; // 스택을 꺼내고 top을 1 감소
}

element peek(Stack* s) { // top에 있는 item 보여주는 함수
	// 스택이 비어있으면 peek 불가, 스택에서 top에 있는 item을 return
	if (is_empty(s)) {
		fprintf(stderr, "스택이 공백입니다!!\n");
		return;
	}
	else return s->stack[s->top];
}

int main(void) {
	Stack s;

	init_stack(&s);
	push(&s, 1);
	push(&s, 3);
	push(&s, 5);
	push(&s, 7);

	for (int i = 0; i < 4; i++) {
		printf("%d ", peek(&s));
	}
	printf("\n");

	for (int i = 0; i < 4; i++) {
		printf("%d ", pop(&s));
	}

	return 0;
}

  • peek함수는 스택의 top을 보여주기만 하는거이기 때문에 스택 top의 item만 return된다
  • pop함수는 스택 top의 item을 가져오면서 스택에서 삭제

연결리스트로 구현한 스택

- 장점 : 크기 제한 X

- 단점 : 구현이 복잡, 삽입/삭제 시간이 오래 걸린다

- 크기에 제한이 없기 때문에 스택이 포화인지 확인할 필요가 없다

 

스택 구조체 정의 (LinkedList)

typedef int element; // 스택 item의 타입

typedef struct stacknode {
	element item; // 스택의 item
	struct stacknode* link; // 스택의 item들을 연결해주는 link
} stacknode;

typedef struct { stacknode의 top을 가리키는 linkedstack
	stacknode* top; 
}linkedstack;

 

1. is_empty

int is_empty(linkedstack* s) {
	if ((s->top) == NULL)
		return 1;
	return 0;
}

 

2. push

  1. push할 새로운 노드(newnode) 동적 할당
  2. newnode의 item칸에 push할 item 저장
  3. newnode의 link칸에 top 주소 저장
  4. top을 newnode로 변경

void push(linkedstack* s, element item) {
	/*
	(1) push할 새로운 노드(newnode) 동적 할당
	(2) newnode의 item칸에 push할 item 저장
	(3) newnode의 link칸에 top 주소 저장
	(4) top을 newnode로 변경
	*/
	stacknode* newnode = (stacknode*)malloc(sizeof(stacknode)); // (1)
	if (newnode == NULL) {
		fprintf(stderr, "메모리 할당 에러");
		return;
	}
	else {
		newnode->item = item; // (2)
		newnode->link = s->top; // (3)
		s->top = newnode; // (4)
	}
}

 

 

3. pop

  1. 삭제할 노드(맨 위 노드) 임의 설정 후, top(맨 위 노드) 주소값 저장
  2. return할 item에 top(맨 위 노드)의 item 저장
  3. top 포인터에 2번째 노드(맨 위 다음 노드)의 주소값 저장
  4. 맨 위 노드 제거 (free)
  5. item return

element pop(linkedstack* s) {
	/*
	(1) 삭제할 노드(맨 위 노드) 임의 설정 후, top(맨 위 노드) 주소값 저장
	(2) return할 item에 top(맨 위 노드)의 item 저장
	(3) top 포인터에 2번째 노드(맨 위 다음 노드)의 주소값 저장
	(4) 맨 위 노드 제거 (free)
	(5) item return
	*/
	if (is_empty(s)) {
		fprintf(stderr, "Error : Stack is empty!!");
		return;
	}
	else {
		stacknode* d_node = s->top; // (1)
		element item = d_node->item; // (2)
		s->top = d_node->link; // (3)
		free(d_node); // (4)
		return item; // (5)
	}
}
  • (1)을 통해서 d_node를 top이라고 임의설정
  • (3)을 통해서 top포인터에 삭제할 노드(맨 위 노드)의 link를 저장               
  • -> top포인터가 맨 위에서 2번째 노드를 가리키게 된다
  • -> 이를 통해서 top 포인터와 맨 위에서 2번째 노드가 새롭게 서로 연결

 

 

4. peek 

element peek(linkedstack* s) {
	if (is_empty(s)) {
		fprintf(stderr, "Error : Stack is empty!!");
		exit(1);
	}
	else
		return s->top->item;
}

 

 

5. Full Code (Stack : LinkedList)

#include <stdio.h>
#include <stdlib.h>

typedef int element; // 스택 item의 타입

typedef struct stacknode {
	element item; // 스택의 item
	struct stacknode* link; // 스택의 item들을 연결해주는 link
} stacknode;

typedef struct { // 연결된 스택의 타입
	stacknode* top;
}linkedstack;

int is_empty(linkedstack* s) {
	if ((s->top) == NULL)
		return 1;
	return 0;
}

void push(linkedstack* s, element item) {
	/*
	(1) push할 새로운 노드(newnode) 동적 할당
	(2) newnode의 item칸에 push할 item 저장
	(3) newnode의 link칸에 top 주소 저장
	(4) top을 newnode로 변경
	*/
	stacknode* newnode = (stacknode*)malloc(sizeof(stacknode)); // (1)
	if (newnode == NULL) {
		fprintf(stderr, "메모리 할당 에러");
		return;
	}
	else {
		newnode->item = item; // (2)
		newnode->link = s->top; // (3)
		s->top = newnode; // (4)
	}
}

element pop(linkedstack* s) {
	/*
	(1) 삭제할 노드(맨 위 노드) 임의 설정 후, top(맨 위 노드) 주소값 저장
	(2) return할 item에 top(맨 위 노드)의 item 저장
	(3) top 포인터에 2번째 노드(맨 위 다음 노드)의 주소값 저장
	(4) 맨 위 노드 제거 (free)
	(5) item return
	*/
	if (is_empty(s)) {
		fprintf(stderr, "Error : Stack is empty!!");
		exit(1);
	}
	else {
		stacknode* d_node = s->top; // (1)을 통해서 d_node를 top이라고 임의설정
		element item = d_node->item; // (2)
		s->top = d_node->link; // (3), top포인터에 삭제할 노드(맨 위 노드)의 link를 저장
		free(d_node); // (4)
		return item; // (5)
	}
}

element peek(linkedstack* s) {
	if (is_empty(s)) {
		fprintf(stderr, "Error : Stack is empty!!");
		exit(1);
	}
	else
		return s->top->item;
}

void printStack(linkedstack* s) {
	if (is_empty(s)) {
		fprintf(stderr, "Error : Stack is empty!!");
		return;
	}
	else {
		stacknode* p_node = s->top;
		while (p_node) {
			printf("%d ", p_node->item);
			p_node = p_node->link;
		}
		printf("\n");
	}
}

int main(void) {
	stacknode* s = (stacknode*)malloc(sizeof(stacknode));
	linkedstack* ls = NULL;

	push(&ls, 10);
	push(&ls, 20);
	push(&ls, 30);
	push(&ls, 40);
	push(&ls, 50);
	
	printf("#####peek 실행#####\n");
	for (int i = 0; i < 5; i++) {
		printf("%d ", peek(&ls));
	}
	printf("\n\n");

	printf("#####printStack 실행#####\n");
	printStack(&ls);
	printf("\n");

	printf("#####pop 실행#####\n");
	for (int i = 0; i < 5; i++) {
		printf("%d ", pop(&ls));
	}
	printf("\n\n");

	printf("#####printStack 실행#####\n");
	printStack(&ls);
	printf("\n");

	return 0;
}